6
6
7
7
namespace Magento \Bundle \Model \Product ;
8
8
9
+ use Magento \Bundle \Model \Option ;
10
+ use Magento \Bundle \Model \ResourceModel \Option \Collection ;
9
11
use Magento \Bundle \Model \ResourceModel \Selection \Collection as Selections ;
10
12
use Magento \Bundle \Model \ResourceModel \Selection \Collection \FilterApplier as SelectionCollectionFilterApplier ;
11
13
use Magento \Catalog \Api \ProductRepositoryInterface ;
@@ -414,16 +416,13 @@ public function beforeSave($product)
414
416
if ($ product ->getCanSaveBundleSelections ()) {
415
417
$ product ->canAffectOptions (true );
416
418
$ 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 ;
427
426
}
428
427
}
429
428
}
@@ -464,7 +463,7 @@ public function getOptionsIds($product)
464
463
public function getOptionsCollection ($ product )
465
464
{
466
465
if (!$ product ->hasData ($ this ->_keyOptionsCollection )) {
467
- /** @var \Magento\Bundle\Model\ResourceModel\Option\ Collection $optionsCollection */
466
+ /** @var Collection $optionsCollection */
468
467
$ optionsCollection = $ this ->_bundleOption ->create ()
469
468
->getResourceCollection ();
470
469
$ optionsCollection ->setProductIdFilter ($ product ->getEntityId ());
@@ -530,10 +529,10 @@ public function getSelectionsCollection($optionIds, $product)
530
529
* Example: the catalog inventory validation of decimal qty can change qty to int,
531
530
* so need to change quote item qty option value too.
532
531
*
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
537
536
* @return $this
538
537
*/
539
538
public function updateQtyOption ($ options , \Magento \Framework \DataObject $ option , $ value , $ product )
@@ -682,6 +681,11 @@ protected function _prepareProduct(\Magento\Framework\DataObject $buyRequest, $p
682
681
$ options
683
682
);
684
683
684
+ $ this ->validateRadioAndSelectOptions (
685
+ $ optionsCollection ,
686
+ $ options
687
+ );
688
+
685
689
$ selectionIds = array_values ($ this ->arrayUtility ->flatten ($ options ));
686
690
// If product has not been configured yet then $selections array should be empty
687
691
if (!empty ($ selectionIds )) {
@@ -1184,9 +1188,11 @@ public function canConfigure($product)
1184
1188
* @return void
1185
1189
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
1186
1190
*/
1191
+ // @codingStandardsIgnoreStart
1187
1192
public function deleteTypeSpecificData (\Magento \Catalog \Model \Product $ product )
1188
1193
{
1189
1194
}
1195
+ // @codingStandardsIgnoreEnd
1190
1196
1191
1197
/**
1192
1198
* Return array of specific to type product entities
@@ -1196,18 +1202,19 @@ public function deleteTypeSpecificData(\Magento\Catalog\Model\Product $product)
1196
1202
*/
1197
1203
public function getIdentities (\Magento \Catalog \Model \Product $ product )
1198
1204
{
1199
- $ identities = parent ::getIdentities ($ product );
1205
+ $ identities = [];
1206
+ $ identities [] = parent ::getIdentities ($ product );
1200
1207
/** @var \Magento\Bundle\Model\Option $option */
1201
1208
foreach ($ this ->getOptions ($ product ) as $ option ) {
1202
1209
if ($ option ->getSelections ()) {
1203
1210
/** @var \Magento\Catalog\Model\Product $selection */
1204
1211
foreach ($ option ->getSelections () as $ selection ) {
1205
- $ identities = array_merge ( $ identities , $ selection ->getIdentities () );
1212
+ $ identities[] = $ selection ->getIdentities ();
1206
1213
}
1207
1214
}
1208
1215
}
1209
1216
1210
- return $ identities ;
1217
+ return array_merge ([], ... $ identities) ;
1211
1218
}
1212
1219
1213
1220
/**
@@ -1272,6 +1279,53 @@ protected function checkIsAllRequiredOptions($product, $isStrictProcessMode, $op
1272
1279
}
1273
1280
}
1274
1281
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
+
1275
1329
/**
1276
1330
* Check if selection is salable
1277
1331
*
@@ -1333,16 +1387,18 @@ protected function checkIsResult($_result)
1333
1387
*/
1334
1388
protected function mergeSelectionsWithOptions ($ options , $ selections )
1335
1389
{
1390
+ $ selections = [];
1391
+
1336
1392
foreach ($ options as $ option ) {
1337
1393
$ optionSelections = $ option ->getSelections ();
1338
1394
if ($ option ->getRequired () && is_array ($ optionSelections ) && count ($ optionSelections ) == 1 ) {
1339
- $ selections = array_merge ( $ selections , $ optionSelections) ;
1395
+ $ selections[] = $ optionSelections ;
1340
1396
} else {
1341
1397
$ selections = [];
1342
1398
break ;
1343
1399
}
1344
1400
}
1345
1401
1346
- return $ selections ;
1402
+ return array_merge ([], ... $ selections) ;
1347
1403
}
1348
1404
}
0 commit comments