12
12
use Magento \Catalog \Model \Product ;
13
13
use Magento \Catalog \Model \Product \Media \Config ;
14
14
use Magento \Catalog \Model \ResourceModel \Product \Gallery ;
15
+ use Magento \Catalog \Model \ResourceModel \Product \MediaGalleryValue ;
16
+ use Magento \Eav \Model \ResourceModel \AttributeValue ;
15
17
use Magento \Framework \App \Filesystem \DirectoryList ;
16
18
use Magento \Framework \App \ObjectManager ;
17
19
use Magento \Framework \EntityManager \MetadataPool ;
30
32
* @api
31
33
*
32
34
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
35
+ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
36
+ * @SuppressWarnings(PHPMD.TooManyFields)
33
37
* @since 101.0.0
34
38
*/
35
39
class CreateHandler implements ExtensionInterface
@@ -90,7 +94,7 @@ class CreateHandler implements ExtensionInterface
90
94
/**
91
95
* @var array
92
96
*/
93
- private $ imagesGallery ;
97
+ private $ mediaEavCache ;
94
98
95
99
/**
96
100
* @var \Magento\Store\Model\StoreManagerInterface
@@ -102,6 +106,21 @@ class CreateHandler implements ExtensionInterface
102
106
*/
103
107
private $ deleteValidator ;
104
108
109
+ /**
110
+ * @var MediaGalleryValue
111
+ */
112
+ private $ mediaGalleryValue ;
113
+
114
+ /**
115
+ * @var AttributeValue
116
+ */
117
+ private AttributeValue $ attributeValue ;
118
+
119
+ /**
120
+ * @var \Magento\Eav\Model\Config
121
+ */
122
+ private $ eavConfig ;
123
+
105
124
/**
106
125
* @var string[]
107
126
*/
@@ -121,7 +140,11 @@ class CreateHandler implements ExtensionInterface
121
140
* @param Database $fileStorageDb
122
141
* @param StoreManagerInterface|null $storeManager
123
142
* @param DeleteValidator|null $deleteValidator
143
+ * @param MediaGalleryValue|null $mediaGalleryValue
144
+ * @param AttributeValue|null $attributeValue
145
+ * @param \Magento\Eav\Model\Config|null $config
124
146
* @throws FileSystemException
147
+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
125
148
*/
126
149
public function __construct (
127
150
MetadataPool $ metadataPool ,
@@ -132,7 +155,10 @@ public function __construct(
132
155
Filesystem $ filesystem ,
133
156
Database $ fileStorageDb ,
134
157
StoreManagerInterface $ storeManager = null ,
135
- ?DeleteValidator $ deleteValidator = null
158
+ ?DeleteValidator $ deleteValidator = null ,
159
+ ?MediaGalleryValue $ mediaGalleryValue = null ,
160
+ ?AttributeValue $ attributeValue = null ,
161
+ ?\Magento \Eav \Model \Config $ config = null
136
162
) {
137
163
$ this ->metadata = $ metadataPool ->getMetadata (\Magento \Catalog \Api \Data \ProductInterface::class);
138
164
$ this ->attributeRepository = $ attributeRepository ;
@@ -143,6 +169,9 @@ public function __construct(
143
169
$ this ->fileStorageDb = $ fileStorageDb ;
144
170
$ this ->storeManager = $ storeManager ?: ObjectManager::getInstance ()->get (StoreManagerInterface::class);
145
171
$ this ->deleteValidator = $ deleteValidator ?: ObjectManager::getInstance ()->get (DeleteValidator::class);
172
+ $ this ->mediaGalleryValue = $ mediaGalleryValue ?? ObjectManager::getInstance ()->get (MediaGalleryValue::class);
173
+ $ this ->attributeValue = $ attributeValue ?? ObjectManager::getInstance ()->get (AttributeValue::class);
174
+ $ this ->eavConfig = $ config ?? ObjectManager::getInstance ()->get (\Magento \Eav \Model \Config::class);
146
175
}
147
176
148
177
/**
@@ -159,6 +188,7 @@ public function __construct(
159
188
*/
160
189
public function execute ($ product , $ arguments = [])
161
190
{
191
+ $ this ->mediaEavCache = null ;
162
192
$ attrCode = $ this ->getAttribute ()->getAttributeCode ();
163
193
164
194
$ value = $ product ->getData ($ attrCode );
@@ -279,14 +309,15 @@ protected function processDeletedImages($product, array &$images)
279
309
*/
280
310
protected function processNewAndExistingImages ($ product , array &$ images )
281
311
{
312
+ $ existingGalleryStoreValues = $ this ->getExistingGalleryStoreValues ($ product );
282
313
foreach ($ images as &$ image ) {
283
314
if (empty ($ image ['removed ' ])) {
284
315
$ isNew = empty ($ image ['value_id ' ]);
285
316
$ data = $ this ->processNewImage ($ product , $ image );
286
317
287
318
// Add per store labels, position, disabled
288
- $ data ['value_id ' ] = $ image ['value_id ' ];
289
- $ data ['label ' ] = isset ($ image ['label ' ]) ? $ image ['label ' ] : '' ;
319
+ $ data ['value_id ' ] = ( int ) $ image ['value_id ' ];
320
+ $ data ['label ' ] = ! empty ($ image ['label ' ]) ? $ image ['label ' ] : null ;
290
321
$ data ['position ' ] = isset ($ image ['position ' ]) && $ image ['position ' ] !== ''
291
322
? (int )$ image ['position ' ]
292
323
: null ;
@@ -295,34 +326,90 @@ protected function processNewAndExistingImages($product, array &$images)
295
326
296
327
$ data [$ this ->metadata ->getLinkField ()] = (int )$ product ->getData ($ this ->metadata ->getLinkField ());
297
328
298
- $ this ->saveGalleryStoreValue ($ product , $ data );
299
- if ($ isNew && $ data ['store_id ' ] !== Store::DEFAULT_STORE_ID ) {
300
- $ dataForDefaultScope = $ data ;
301
- $ dataForDefaultScope ['store_id ' ] = Store::DEFAULT_STORE_ID ;
302
- $ dataForDefaultScope ['disabled ' ] = 0 ;
303
- $ dataForDefaultScope ['label ' ] = null ;
304
- $ this ->saveGalleryStoreValue ($ product , $ dataForDefaultScope );
329
+ if ($ isNew || $ this ->hasGalleryStoreValueChanged ($ data , $ existingGalleryStoreValues )) {
330
+ $ this ->saveGalleryStoreValue ($ product , $ data , $ isNew );
305
331
}
306
332
}
307
333
}
308
334
}
309
335
336
+ /**
337
+ * Get existing gallery store values
338
+ *
339
+ * @param Product $product
340
+ * @return array
341
+ * @throws \Exception
342
+ */
343
+ private function getExistingGalleryStoreValues (Product $ product ): array
344
+ {
345
+ $ existingMediaGalleryValues = [];
346
+ if (!$ product ->isObjectNew ()) {
347
+ $ productId = (int )$ product ->getData ($ this ->metadata ->getLinkField ());
348
+ foreach ($ this ->mediaGalleryValue ->getAllByEntityId ($ productId ) as $ data ) {
349
+ $ existingMediaGalleryValues [] = [
350
+ 'value_id ' => (int ) $ data ['value_id ' ],
351
+ 'store_id ' => (int ) $ data ['store_id ' ],
352
+ 'label ' => $ data ['label ' ] ?: null ,
353
+ 'position ' => $ data ['position ' ] !== null ? (int )$ data ['position ' ] : null ,
354
+ 'disabled ' => (int ) $ data ['disabled ' ],
355
+ ];
356
+ }
357
+ }
358
+ return $ existingMediaGalleryValues ;
359
+ }
360
+
361
+ /**
362
+ * Check if gallery store value has changed
363
+ *
364
+ * @param array $data
365
+ * @param array $existingGalleryStoreValues
366
+ * @return bool
367
+ */
368
+ private function hasGalleryStoreValueChanged (array $ data , array $ existingGalleryStoreValues ): bool
369
+ {
370
+ foreach ($ existingGalleryStoreValues as $ existingGalleryStoreValue ) {
371
+ if ($ existingGalleryStoreValue ['value_id ' ] === $ data ['value_id ' ]
372
+ && $ existingGalleryStoreValue ['store_id ' ] === $ data ['store_id ' ]
373
+ && $ existingGalleryStoreValue ['label ' ] === $ data ['label ' ]
374
+ && $ existingGalleryStoreValue ['position ' ] === $ data ['position ' ]
375
+ && $ existingGalleryStoreValue ['disabled ' ] === $ data ['disabled ' ]
376
+ ) {
377
+ return false ;
378
+ }
379
+ }
380
+
381
+ return true ;
382
+ }
383
+
310
384
/**
311
385
* Save media gallery store value
312
386
*
313
387
* @param Product $product
314
388
* @param array $data
389
+ * @param bool $isNewImage
315
390
*/
316
- private function saveGalleryStoreValue (Product $ product , array $ data ): void
391
+ private function saveGalleryStoreValue (Product $ product , array $ data, bool $ isNewImage ): void
317
392
{
318
- if (!$ product ->isObjectNew ()) {
319
- $ this ->resourceModel ->deleteGalleryValueInStore (
320
- $ data ['value_id ' ],
321
- $ data [$ this ->metadata ->getLinkField ()],
322
- $ data ['store_id ' ]
323
- );
393
+ $ items = [];
394
+ $ items [] = $ data ;
395
+ if ($ isNewImage && $ data ['store_id ' ] !== Store::DEFAULT_STORE_ID ) {
396
+ $ dataForDefaultScope = $ data ;
397
+ $ dataForDefaultScope ['store_id ' ] = Store::DEFAULT_STORE_ID ;
398
+ $ dataForDefaultScope ['disabled ' ] = 0 ;
399
+ $ dataForDefaultScope ['label ' ] = null ;
400
+ $ items [] = $ dataForDefaultScope ;
401
+ }
402
+
403
+ foreach ($ items as $ item ) {
404
+ if (!$ product ->isObjectNew ()) {
405
+ $ this ->resourceModel ->deleteGalleryValueInStore (
406
+ $ item ['value_id ' ],
407
+ $ item [$ this ->metadata ->getLinkField ()],
408
+ $ item ['store_id ' ]
409
+ );
410
+ }
411
+ $ this ->resourceModel ->insertGalleryValueInStore ($ item );
324
412
}
325
- $ this ->resourceModel ->insertGalleryValueInStore ($ data );
326
413
}
327
414
328
415
/**
@@ -530,29 +617,26 @@ private function processMediaAttribute(
530
617
array $ clearImages ,
531
618
array $ newImages
532
619
): void {
533
- $ storeId = $ product ->isObjectNew () ? Store::DEFAULT_STORE_ID : (int ) $ product ->getStoreId ();
534
- /***
535
- * Attributes values are saved as default value in single store mode
536
- * @see \Magento\Catalog\Model\ResourceModel\AbstractResource::_saveAttributeValue
537
- */
538
- if ($ storeId === Store::DEFAULT_STORE_ID
539
- || $ this ->storeManager ->hasSingleStore ()
540
- || $ this ->getMediaAttributeStoreValue ($ product , $ mediaAttrCode , $ storeId ) !== null
541
- ) {
542
- $ value = $ product ->getData ($ mediaAttrCode );
620
+ $ storeId = $ this ->getStoreIdForUpdate ($ product );
621
+ $ oldValue = $ this ->getMediaAttributeStoreValue ($ product , $ mediaAttrCode , $ storeId );
622
+ // Prevent from breaking store inheritance
623
+ if ($ oldValue !== false || $ storeId === Store::DEFAULT_STORE_ID ) {
624
+ $ value = $ product ->hasData ($ mediaAttrCode ) ? $ product ->getData ($ mediaAttrCode ) : $ oldValue ;
543
625
$ newValue = $ value ;
544
626
if (in_array ($ value , $ clearImages )) {
545
627
$ newValue = 'no_selection ' ;
546
628
}
547
629
if (in_array ($ value , array_keys ($ newImages ))) {
548
630
$ newValue = $ newImages [$ value ]['new_file ' ];
549
631
}
550
- $ product ->setData ($ mediaAttrCode , $ newValue );
551
- $ product ->addAttributeUpdate (
552
- $ mediaAttrCode ,
553
- $ newValue ,
554
- $ storeId
555
- );
632
+ if ($ oldValue !== $ newValue ) {
633
+ $ product ->setData ($ mediaAttrCode , $ newValue );
634
+ $ product ->addAttributeUpdate (
635
+ $ mediaAttrCode ,
636
+ $ newValue ,
637
+ $ storeId
638
+ );
639
+ }
556
640
}
557
641
}
558
642
@@ -565,6 +649,7 @@ private function processMediaAttribute(
565
649
* @param array $newImages
566
650
* @param array $existImages
567
651
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
652
+ * @SuppressWarnings(PHPMD.NPathComplexity)
568
653
*/
569
654
private function processMediaAttributeLabel (
570
655
Product $ product ,
@@ -573,6 +658,9 @@ private function processMediaAttributeLabel(
573
658
array $ newImages ,
574
659
array $ existImages
575
660
): void {
661
+ $ storeId = $ this ->getStoreIdForUpdate ($ product );
662
+ $ oldAttrLabelValue = $ this ->getMediaAttributeStoreValue ($ product , $ mediaAttrCode . '_label ' , $ storeId );
663
+
576
664
$ resetLabel = false ;
577
665
$ attrData = $ product ->getData ($ mediaAttrCode );
578
666
if (in_array ($ attrData , $ clearImages )) {
@@ -595,33 +683,58 @@ private function processMediaAttributeLabel(
595
683
$ product ->setData ($ mediaAttrCode . '_label ' , null );
596
684
$ resetLabel = true ;
597
685
}
598
- if (!empty ($ product ->getData ($ mediaAttrCode . '_label ' ))
599
- || $ resetLabel === true
600
- ) {
686
+
687
+ $ newAttrLabelValue = $ product ->getData ($ mediaAttrCode . '_label ' );
688
+
689
+ if ($ newAttrLabelValue !== $ oldAttrLabelValue && ($ resetLabel || !empty ($ newAttrLabelValue ))) {
601
690
$ product ->addAttributeUpdate (
602
691
$ mediaAttrCode . '_label ' ,
603
- $ product -> getData ( $ mediaAttrCode . ' _label ' ) ,
604
- $ product -> getStoreId ()
692
+ $ newAttrLabelValue ,
693
+ $ storeId
605
694
);
606
695
}
607
696
}
608
697
609
698
/**
610
- * Get product images for all stores
699
+ * Get store id to update media attribute
611
700
*
612
- * @param ProductInterface $product
613
- * @return array
701
+ * Attributes values are saved in "all store views" in single store mode
702
+ *
703
+ * @param Product $product
704
+ * @return int
705
+ * @see \Magento\Catalog\Model\ResourceModel\AbstractResource::_saveAttributeValue
614
706
*/
615
- private function getImagesForAllStores ( ProductInterface $ product )
707
+ private function getStoreIdForUpdate ( Product $ product ): int
616
708
{
617
- if ($ this ->imagesGallery === null ) {
618
- $ storeIds = array_keys ($ this ->storeManager ->getStores ());
619
- $ storeIds [] = 0 ;
709
+ return $ product ->isObjectNew () || $ this ->storeManager ->hasSingleStore ()
710
+ ? Store::DEFAULT_STORE_ID
711
+ : (int ) $ product ->getStoreId ();
712
+ }
620
713
621
- $ this ->imagesGallery = $ this ->resourceModel ->getProductImages ($ product , $ storeIds );
714
+ /**
715
+ * Get all media attributes values
716
+ *
717
+ * @param Product $product
718
+ * @return array
719
+ */
720
+ private function getMediaAttributesValues (Product $ product ): array
721
+ {
722
+ if ($ this ->mediaEavCache === null ) {
723
+ $ attributeCodes = [];
724
+ foreach ($ this ->mediaConfig ->getMediaAttributeCodes () as $ attributeCode ) {
725
+ $ attributeCodes [] = $ attributeCode ;
726
+ if (in_array ($ attributeCode , $ this ->mediaAttributesWithLabels )) {
727
+ $ attributeCodes [] = $ attributeCode . '_label ' ;
728
+ }
729
+ }
730
+ $ this ->mediaEavCache = $ this ->attributeValue ->getValues (
731
+ ProductInterface::class,
732
+ (int ) $ product ->getData ($ this ->metadata ->getLinkField ()),
733
+ $ attributeCodes
734
+ );
622
735
}
623
736
624
- return $ this ->imagesGallery ;
737
+ return $ this ->mediaEavCache ;
625
738
}
626
739
627
740
/**
@@ -630,18 +743,22 @@ private function getImagesForAllStores(ProductInterface $product)
630
743
* @param Product $product
631
744
* @param string $attributeCode
632
745
* @param int|null $storeId
633
- * @return string|null
746
+ * @return mixed|false
634
747
*/
635
- private function getMediaAttributeStoreValue (Product $ product , string $ attributeCode , int $ storeId = null ): ?string
636
- {
637
- $ gallery = $ this ->getImagesForAllStores ($ product );
748
+ private function getMediaAttributeStoreValue (
749
+ Product $ product ,
750
+ string $ attributeCode ,
751
+ int $ storeId = null
752
+ ): mixed {
753
+ $ attributes = $ this ->eavConfig ->getEntityAttributes (Product::ENTITY );
754
+ $ attributeId = $ attributes [$ attributeCode ]->getAttributeId ();
638
755
$ storeId = $ storeId === null ? (int ) $ product ->getStoreId () : $ storeId ;
639
- foreach ($ gallery as $ image ) {
640
- if ($ image [ ' attribute_code ' ] === $ attributeCode && ((int )$ image ['store_id ' ]) === $ storeId ) {
641
- return $ image [ ' filepath ' ];
756
+ foreach ($ this -> getMediaAttributesValues ( $ product ) as $ value ) {
757
+ if ($ value [ ' attribute_id ' ] === $ attributeId && ((int )$ value ['store_id ' ]) === $ storeId ) {
758
+ return $ value [ ' value ' ];
642
759
}
643
760
}
644
- return null ;
761
+ return false ;
645
762
}
646
763
647
764
/**
0 commit comments