1212use Magento \Catalog \Model \Product ;
1313use Magento \Catalog \Model \Product \Media \Config ;
1414use Magento \Catalog \Model \ResourceModel \Product \Gallery ;
15+ use Magento \Catalog \Model \ResourceModel \Product \MediaGalleryValue ;
16+ use Magento \Eav \Model \ResourceModel \AttributeValue ;
1517use Magento \Framework \App \Filesystem \DirectoryList ;
1618use Magento \Framework \App \ObjectManager ;
1719use Magento \Framework \EntityManager \MetadataPool ;
3032 * @api
3133 *
3234 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
35+ * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
36+ * @SuppressWarnings(PHPMD.TooManyFields)
3337 * @since 101.0.0
3438 */
3539class CreateHandler implements ExtensionInterface
@@ -90,7 +94,7 @@ class CreateHandler implements ExtensionInterface
9094 /**
9195 * @var array
9296 */
93- private $ imagesGallery ;
97+ private $ mediaEavCache ;
9498
9599 /**
96100 * @var \Magento\Store\Model\StoreManagerInterface
@@ -102,6 +106,21 @@ class CreateHandler implements ExtensionInterface
102106 */
103107 private $ deleteValidator ;
104108
109+ /**
110+ * @var MediaGalleryValue
111+ */
112+ private $ mediaGalleryValue ;
113+
114+ /**
115+ * @var AttributeValue
116+ */
117+ private $ attributeValue ;
118+
119+ /**
120+ * @var \Magento\Eav\Model\Config
121+ */
122+ private $ eavConfig ;
123+
105124 /**
106125 * @var string[]
107126 */
@@ -121,7 +140,11 @@ class CreateHandler implements ExtensionInterface
121140 * @param Database $fileStorageDb
122141 * @param StoreManagerInterface|null $storeManager
123142 * @param DeleteValidator|null $deleteValidator
143+ * @param MediaGalleryValue|null $mediaGalleryValue
144+ * @param AttributeValue|null $attributeValue
145+ * @param \Magento\Eav\Model\Config|null $config
124146 * @throws FileSystemException
147+ * @SuppressWarnings(PHPMD.ExcessiveParameterList)
125148 */
126149 public function __construct (
127150 MetadataPool $ metadataPool ,
@@ -132,7 +155,10 @@ public function __construct(
132155 Filesystem $ filesystem ,
133156 Database $ fileStorageDb ,
134157 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
136162 ) {
137163 $ this ->metadata = $ metadataPool ->getMetadata (\Magento \Catalog \Api \Data \ProductInterface::class);
138164 $ this ->attributeRepository = $ attributeRepository ;
@@ -143,6 +169,9 @@ public function __construct(
143169 $ this ->fileStorageDb = $ fileStorageDb ;
144170 $ this ->storeManager = $ storeManager ?: ObjectManager::getInstance ()->get (StoreManagerInterface::class);
145171 $ 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);
146175 }
147176
148177 /**
@@ -159,6 +188,7 @@ public function __construct(
159188 */
160189 public function execute ($ product , $ arguments = [])
161190 {
191+ $ this ->mediaEavCache = null ;
162192 $ attrCode = $ this ->getAttribute ()->getAttributeCode ();
163193
164194 $ value = $ product ->getData ($ attrCode );
@@ -279,14 +309,15 @@ protected function processDeletedImages($product, array &$images)
279309 */
280310 protected function processNewAndExistingImages ($ product , array &$ images )
281311 {
312+ $ existingGalleryStoreValues = $ this ->getExistingGalleryStoreValues ($ product );
282313 foreach ($ images as &$ image ) {
283314 if (empty ($ image ['removed ' ])) {
284315 $ isNew = empty ($ image ['value_id ' ]);
285316 $ data = $ this ->processNewImage ($ product , $ image );
286317
287318 // 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 ;
290321 $ data ['position ' ] = isset ($ image ['position ' ]) && $ image ['position ' ] !== ''
291322 ? (int )$ image ['position ' ]
292323 : null ;
@@ -295,34 +326,78 @@ protected function processNewAndExistingImages($product, array &$images)
295326
296327 $ data [$ this ->metadata ->getLinkField ()] = (int )$ product ->getData ($ this ->metadata ->getLinkField ());
297328
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 ) {
330+ $ data += (array ) $ this ->getExistingGalleryStoreValue (
331+ $ existingGalleryStoreValues ,
332+ $ data ['value_id ' ],
333+ $ data ['store_id ' ]
334+ );
305335 }
336+
337+ $ this ->saveGalleryStoreValue ($ data , $ isNew );
306338 }
307339 }
308340 }
309341
310342 /**
311- * Save media gallery store value
343+ * Returns existing gallery store value by value id and store id
344+ *
345+ * @param array $existingGalleryStoreValues
346+ * @param int $valueId
347+ * @param int $storeId
348+ * @return array|null
349+ */
350+ private function getExistingGalleryStoreValue (array $ existingGalleryStoreValues , int $ valueId , int $ storeId ): ?array
351+ {
352+ foreach ($ existingGalleryStoreValues as $ existingGalleryStoreValue ) {
353+ if (((int ) $ existingGalleryStoreValue ['value_id ' ]) === $ valueId
354+ && ((int ) $ existingGalleryStoreValue ['store_id ' ]) === $ storeId
355+ ) {
356+ return $ existingGalleryStoreValue ;
357+ }
358+ }
359+
360+ return null ;
361+ }
362+
363+ /**
364+ * Get existing gallery store values
312365 *
313366 * @param Product $product
314- * @param array $data
367+ * @return array
368+ * @throws \Exception
315369 */
316- private function saveGalleryStoreValue (Product $ product, array $ data ): void
370+ private function getExistingGalleryStoreValues (Product $ product ): array
317371 {
372+ $ existingMediaGalleryValues = [];
318373 if (!$ product ->isObjectNew ()) {
319- $ this ->resourceModel ->deleteGalleryValueInStore (
320- $ data ['value_id ' ],
321- $ data [$ this ->metadata ->getLinkField ()],
322- $ data ['store_id ' ]
323- );
374+ $ productId = (int )$ product ->getData ($ this ->metadata ->getLinkField ());
375+ $ existingMediaGalleryValues = $ this ->mediaGalleryValue ->getAllByEntityId ($ productId );
376+ }
377+ return $ existingMediaGalleryValues ;
378+ }
379+
380+ /**
381+ * Save media gallery store value
382+ *
383+ * @param array $data
384+ * @param bool $isNewImage
385+ */
386+ private function saveGalleryStoreValue (array $ data , bool $ isNewImage ): void
387+ {
388+ $ items = [];
389+ $ items [] = $ data ;
390+ if ($ isNewImage && $ data ['store_id ' ] !== Store::DEFAULT_STORE_ID ) {
391+ $ dataForDefaultScope = $ data ;
392+ $ dataForDefaultScope ['store_id ' ] = Store::DEFAULT_STORE_ID ;
393+ $ dataForDefaultScope ['disabled ' ] = 0 ;
394+ $ dataForDefaultScope ['label ' ] = null ;
395+ $ items [] = $ dataForDefaultScope ;
396+ }
397+
398+ foreach ($ items as $ item ) {
399+ $ this ->mediaGalleryValue ->saveGalleryStoreValue ($ item );
324400 }
325- $ this ->resourceModel ->insertGalleryValueInStore ($ data );
326401 }
327402
328403 /**
@@ -530,29 +605,26 @@ private function processMediaAttribute(
530605 array $ clearImages ,
531606 array $ newImages
532607 ): 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 );
608+ $ storeId = $ this ->getStoreIdForUpdate ($ product );
609+ $ oldValue = $ this ->getMediaAttributeStoreValue ($ product , $ mediaAttrCode , $ storeId );
610+ // Prevent from breaking store inheritance
611+ if ($ oldValue !== false || $ storeId === Store::DEFAULT_STORE_ID ) {
612+ $ value = $ product ->hasData ($ mediaAttrCode ) ? $ product ->getData ($ mediaAttrCode ) : $ oldValue ;
543613 $ newValue = $ value ;
544614 if (in_array ($ value , $ clearImages )) {
545615 $ newValue = 'no_selection ' ;
546616 }
547617 if (in_array ($ value , array_keys ($ newImages ))) {
548618 $ newValue = $ newImages [$ value ]['new_file ' ];
549619 }
550- $ product ->setData ($ mediaAttrCode , $ newValue );
551- $ product ->addAttributeUpdate (
552- $ mediaAttrCode ,
553- $ newValue ,
554- $ storeId
555- );
620+ if ($ oldValue !== $ newValue ) {
621+ $ product ->setData ($ mediaAttrCode , $ newValue );
622+ $ product ->addAttributeUpdate (
623+ $ mediaAttrCode ,
624+ $ newValue ,
625+ $ storeId
626+ );
627+ }
556628 }
557629 }
558630
@@ -565,6 +637,7 @@ private function processMediaAttribute(
565637 * @param array $newImages
566638 * @param array $existImages
567639 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
640+ * @SuppressWarnings(PHPMD.NPathComplexity)
568641 */
569642 private function processMediaAttributeLabel (
570643 Product $ product ,
@@ -573,6 +646,9 @@ private function processMediaAttributeLabel(
573646 array $ newImages ,
574647 array $ existImages
575648 ): void {
649+ $ storeId = $ this ->getStoreIdForUpdate ($ product );
650+ $ oldAttrLabelValue = $ this ->getMediaAttributeStoreValue ($ product , $ mediaAttrCode . '_label ' , $ storeId );
651+
576652 $ resetLabel = false ;
577653 $ attrData = $ product ->getData ($ mediaAttrCode );
578654 if (in_array ($ attrData , $ clearImages )) {
@@ -595,33 +671,58 @@ private function processMediaAttributeLabel(
595671 $ product ->setData ($ mediaAttrCode . '_label ' , null );
596672 $ resetLabel = true ;
597673 }
598- if (!empty ($ product ->getData ($ mediaAttrCode . '_label ' ))
599- || $ resetLabel === true
600- ) {
674+
675+ $ newAttrLabelValue = $ product ->getData ($ mediaAttrCode . '_label ' );
676+
677+ if ($ newAttrLabelValue !== $ oldAttrLabelValue && ($ resetLabel || !empty ($ newAttrLabelValue ))) {
601678 $ product ->addAttributeUpdate (
602679 $ mediaAttrCode . '_label ' ,
603- $ product -> getData ( $ mediaAttrCode . ' _label ' ) ,
604- $ product -> getStoreId ()
680+ $ newAttrLabelValue ,
681+ $ storeId
605682 );
606683 }
607684 }
608685
609686 /**
610- * Get product images for all stores
687+ * Get store id to update media attribute
611688 *
612- * @param ProductInterface $product
613- * @return array
689+ * Attributes values are saved in "all store views" in single store mode
690+ *
691+ * @param Product $product
692+ * @return int
693+ * @see \Magento\Catalog\Model\ResourceModel\AbstractResource::_saveAttributeValue
614694 */
615- private function getImagesForAllStores ( ProductInterface $ product )
695+ private function getStoreIdForUpdate ( Product $ product ): int
616696 {
617- if ($ this ->imagesGallery === null ) {
618- $ storeIds = array_keys ($ this ->storeManager ->getStores ());
619- $ storeIds [] = 0 ;
697+ return $ product ->isObjectNew () || $ this ->storeManager ->hasSingleStore ()
698+ ? Store::DEFAULT_STORE_ID
699+ : (int ) $ product ->getStoreId ();
700+ }
620701
621- $ this ->imagesGallery = $ this ->resourceModel ->getProductImages ($ product , $ storeIds );
702+ /**
703+ * Get all media attributes values
704+ *
705+ * @param Product $product
706+ * @return array
707+ */
708+ private function getMediaAttributesValues (Product $ product ): array
709+ {
710+ if ($ this ->mediaEavCache === null ) {
711+ $ attributeCodes = [];
712+ foreach ($ this ->mediaConfig ->getMediaAttributeCodes () as $ attributeCode ) {
713+ $ attributeCodes [] = $ attributeCode ;
714+ if (in_array ($ attributeCode , $ this ->mediaAttributesWithLabels )) {
715+ $ attributeCodes [] = $ attributeCode . '_label ' ;
716+ }
717+ }
718+ $ this ->mediaEavCache = $ this ->attributeValue ->getValues (
719+ ProductInterface::class,
720+ (int ) $ product ->getData ($ this ->metadata ->getLinkField ()),
721+ $ attributeCodes
722+ );
622723 }
623724
624- return $ this ->imagesGallery ;
725+ return $ this ->mediaEavCache ;
625726 }
626727
627728 /**
@@ -630,18 +731,22 @@ private function getImagesForAllStores(ProductInterface $product)
630731 * @param Product $product
631732 * @param string $attributeCode
632733 * @param int|null $storeId
633- * @return string|null
734+ * @return mixed|false
634735 */
635- private function getMediaAttributeStoreValue (Product $ product , string $ attributeCode , int $ storeId = null ): ?string
636- {
637- $ gallery = $ this ->getImagesForAllStores ($ product );
736+ private function getMediaAttributeStoreValue (
737+ Product $ product ,
738+ string $ attributeCode ,
739+ int $ storeId = null
740+ ): mixed {
741+ $ attributes = $ this ->eavConfig ->getEntityAttributes (Product::ENTITY );
742+ $ attributeId = $ attributes [$ attributeCode ]->getAttributeId ();
638743 $ 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 ' ];
744+ foreach ($ this -> getMediaAttributesValues ( $ product ) as $ value ) {
745+ if ($ value [ ' attribute_id ' ] === $ attributeId && ((int )$ value ['store_id ' ]) === $ storeId ) {
746+ return $ value [ ' value ' ];
642747 }
643748 }
644- return null ;
749+ return false ;
645750 }
646751
647752 /**
0 commit comments