66
77namespace Magento \Bundle \Model \Product ;
88
9+ use Magento \Bundle \Model \Option ;
10+ use Magento \Bundle \Model \ResourceModel \Option \Collection ;
911use Magento \Bundle \Model \ResourceModel \Selection \Collection as Selections ;
1012use Magento \Bundle \Model \ResourceModel \Selection \Collection \FilterApplier as SelectionCollectionFilterApplier ;
1113use Magento \Catalog \Api \ProductRepositoryInterface ;
@@ -414,16 +416,13 @@ public function beforeSave($product)
414416 if ($ product ->getCanSaveBundleSelections ()) {
415417 $ product ->canAffectOptions (true );
416418 $ selections = $ product ->getBundleSelectionsData ();
417- if ($ selections && !empty ($ selections )) {
418- $ options = $ product ->getBundleOptionsData ();
419- if ($ options ) {
420- foreach ($ options as $ option ) {
421- if (empty ($ option ['delete ' ]) || 1 != (int )$ option ['delete ' ]) {
422- $ product ->setTypeHasOptions (true );
423- if (1 == (int )$ option ['required ' ]) {
424- $ product ->setTypeHasRequiredOptions (true );
425- break ;
426- }
419+ if (!empty ($ selections ) && $ options = $ product ->getBundleOptionsData ()) {
420+ foreach ($ options as $ option ) {
421+ if (empty ($ option ['delete ' ]) || 1 != (int )$ option ['delete ' ]) {
422+ $ product ->setTypeHasOptions (true );
423+ if (1 == (int )$ option ['required ' ]) {
424+ $ product ->setTypeHasRequiredOptions (true );
425+ break ;
427426 }
428427 }
429428 }
@@ -464,7 +463,7 @@ public function getOptionsIds($product)
464463 public function getOptionsCollection ($ product )
465464 {
466465 if (!$ product ->hasData ($ this ->_keyOptionsCollection )) {
467- /** @var \Magento\Bundle\Model\ResourceModel\Option\ Collection $optionsCollection */
466+ /** @var Collection $optionsCollection */
468467 $ optionsCollection = $ this ->_bundleOption ->create ()
469468 ->getResourceCollection ();
470469 $ optionsCollection ->setProductIdFilter ($ product ->getEntityId ());
@@ -530,10 +529,10 @@ public function getSelectionsCollection($optionIds, $product)
530529 * Example: the catalog inventory validation of decimal qty can change qty to int,
531530 * so need to change quote item qty option value too.
532531 *
533- * @param array $options
534- * @param \Magento\Framework\DataObject $option
535- * @param mixed $value
536- * @param \Magento\Catalog\Model\Product $product
532+ * @param array $options
533+ * @param \Magento\Framework\DataObject $option
534+ * @param mixed $value
535+ * @param \Magento\Catalog\Model\Product $product
537536 * @return $this
538537 */
539538 public function updateQtyOption ($ options , \Magento \Framework \DataObject $ option , $ value , $ product )
@@ -682,6 +681,11 @@ protected function _prepareProduct(\Magento\Framework\DataObject $buyRequest, $p
682681 $ options
683682 );
684683
684+ $ this ->validateRadioAndSelectOptions (
685+ $ optionsCollection ,
686+ $ options
687+ );
688+
685689 $ selectionIds = array_values ($ this ->arrayUtility ->flatten ($ options ));
686690 // If product has not been configured yet then $selections array should be empty
687691 if (!empty ($ selectionIds )) {
@@ -1184,9 +1188,11 @@ public function canConfigure($product)
11841188 * @return void
11851189 * @SuppressWarnings(PHPMD.UnusedFormalParameter)
11861190 */
1191+ // @codingStandardsIgnoreStart
11871192 public function deleteTypeSpecificData (\Magento \Catalog \Model \Product $ product )
11881193 {
11891194 }
1195+ // @codingStandardsIgnoreEnd
11901196
11911197 /**
11921198 * Return array of specific to type product entities
@@ -1196,18 +1202,19 @@ public function deleteTypeSpecificData(\Magento\Catalog\Model\Product $product)
11961202 */
11971203 public function getIdentities (\Magento \Catalog \Model \Product $ product )
11981204 {
1199- $ identities = parent ::getIdentities ($ product );
1205+ $ identities = [];
1206+ $ identities [] = parent ::getIdentities ($ product );
12001207 /** @var \Magento\Bundle\Model\Option $option */
12011208 foreach ($ this ->getOptions ($ product ) as $ option ) {
12021209 if ($ option ->getSelections ()) {
12031210 /** @var \Magento\Catalog\Model\Product $selection */
12041211 foreach ($ option ->getSelections () as $ selection ) {
1205- $ identities = array_merge ( $ identities , $ selection ->getIdentities () );
1212+ $ identities[] = $ selection ->getIdentities ();
12061213 }
12071214 }
12081215 }
12091216
1210- return $ identities ;
1217+ return array_merge ([], ... $ identities) ;
12111218 }
12121219
12131220 /**
@@ -1272,6 +1279,53 @@ protected function checkIsAllRequiredOptions($product, $isStrictProcessMode, $op
12721279 }
12731280 }
12741281
1282+ /**
1283+ * Validate Options for Radio and Select input types
1284+ *
1285+ * @param Collection $optionsCollection
1286+ * @param int[] $options
1287+ * @return void
1288+ * @throws \Magento\Framework\Exception\LocalizedException
1289+ */
1290+ private function validateRadioAndSelectOptions ($ optionsCollection , $ options ): void
1291+ {
1292+ $ errorTypes = [];
1293+
1294+ if (is_array ($ optionsCollection ->getItems ())) {
1295+ foreach ($ optionsCollection ->getItems () as $ option ) {
1296+ if ($ this ->isSelectedOptionValid ($ option , $ options )) {
1297+ $ errorTypes [] = $ option ->getType ();
1298+ }
1299+ }
1300+ }
1301+
1302+ if (!empty ($ errorTypes )) {
1303+ throw new \Magento \Framework \Exception \LocalizedException (
1304+ __ (
1305+ 'Option type (%types) should have only one element. ' ,
1306+ ['types ' => implode (", " , $ errorTypes )]
1307+ )
1308+ );
1309+ }
1310+ }
1311+
1312+ /**
1313+ * Check if selected option is valid
1314+ *
1315+ * @param Option $option
1316+ * @param array $options
1317+ * @return bool
1318+ */
1319+ private function isSelectedOptionValid ($ option , $ options ): bool
1320+ {
1321+ return (
1322+ ($ option ->getType () == 'radio ' || $ option ->getType () == 'select ' ) &&
1323+ isset ($ options [$ option ->getOptionId ()]) &&
1324+ is_array ($ options [$ option ->getOptionId ()]) &&
1325+ count ($ options [$ option ->getOptionId ()]) > 1
1326+ );
1327+ }
1328+
12751329 /**
12761330 * Check if selection is salable
12771331 *
@@ -1333,16 +1387,18 @@ protected function checkIsResult($_result)
13331387 */
13341388 protected function mergeSelectionsWithOptions ($ options , $ selections )
13351389 {
1390+ $ selections = [];
1391+
13361392 foreach ($ options as $ option ) {
13371393 $ optionSelections = $ option ->getSelections ();
13381394 if ($ option ->getRequired () && is_array ($ optionSelections ) && count ($ optionSelections ) == 1 ) {
1339- $ selections = array_merge ( $ selections , $ optionSelections) ;
1395+ $ selections[] = $ optionSelections ;
13401396 } else {
13411397 $ selections = [];
13421398 break ;
13431399 }
13441400 }
13451401
1346- return $ selections ;
1402+ return array_merge ([], ... $ selections) ;
13471403 }
13481404}
0 commit comments