Skip to content

Commit d1e4bbe

Browse files
committed
MC-29916: [Magento Cloud] Configurable product images missing in the admin
1 parent 4165276 commit d1e4bbe

File tree

3 files changed

+270
-108
lines changed

3 files changed

+270
-108
lines changed

app/code/Magento/CatalogImportExport/Model/Import/Product/MediaGalleryProcessor.php

Lines changed: 158 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -108,40 +108,161 @@ public function __construct(
108108
public function saveMediaGallery(array $mediaGalleryData)
109109
{
110110
$this->initMediaGalleryResources();
111-
$mediaGalleryDataGlobal = array_replace_recursive(...$mediaGalleryData);
112-
$imageNames = [];
113-
$multiInsertData = [];
114-
$valueToProductId = [];
115-
foreach ($mediaGalleryDataGlobal as $productSku => $mediaGalleryRows) {
116-
$productId = $this->skuProcessor->getNewSku($productSku)[$this->getProductEntityLinkField()];
117-
$insertedGalleryImgs = [];
118-
foreach ($mediaGalleryRows as $insertValue) {
119-
if (!in_array($insertValue['value'], $insertedGalleryImgs)) {
120-
$valueArr = [
121-
'attribute_id' => $insertValue['attribute_id'],
122-
'value' => $insertValue['value'],
111+
$mediaGalleryValues = [];
112+
$mediaGalleryValueData = [];
113+
$productMediaGalleryValueData = [];
114+
$mediaGalleryValueToEntityData = [];
115+
$mediaGalleryValueToStoreData = [];
116+
$productLinkIdField = $this->getProductEntityLinkField();
117+
foreach ($mediaGalleryData as $storeId => $storeMediaGalleryData) {
118+
foreach ($storeMediaGalleryData as $sku => $productMediaGalleryData) {
119+
$productId = $this->skuProcessor->getNewSku($sku)[$productLinkIdField];
120+
$productMediaGalleryValueData[$productId] = $productMediaGalleryValueData[$productId] ?? [];
121+
foreach ($productMediaGalleryData as $data) {
122+
if (!in_array($data['value'], $productMediaGalleryValueData[$productId])) {
123+
$productMediaGalleryValueData[$productId][] = $data['value'];
124+
$mediaGalleryValueData[] = [
125+
'attribute_id' => $data['attribute_id'],
126+
'value' => $data['value'],
127+
];
128+
$mediaGalleryValueToEntityData[] = [
129+
'value' => $data['value'],
130+
$productLinkIdField => $productId,
131+
];
132+
}
133+
$mediaGalleryValues[] = $data['value'];
134+
$mediaGalleryValueToStoreData[] = [
135+
'value' => $data['value'],
136+
'store_id' => $storeId,
137+
$productLinkIdField => $productId,
138+
'label' => $data['label'],
139+
'position' => $data['position'],
140+
'disabled' => $data['disabled'],
123141
];
124-
$valueToProductId[$insertValue['value']][] = $productId;
125-
$imageNames[] = $insertValue['value'];
126-
$multiInsertData[] = $valueArr;
127-
$insertedGalleryImgs[] = $insertValue['value'];
128142
}
129143
}
130144
}
131-
$oldMediaValues = $this->connection->fetchAssoc(
132-
$this->connection->select()->from($this->mediaGalleryTableName, ['value_id', 'value'])
133-
->where('value IN (?)', $imageNames)
134-
);
135-
$this->connection->insertOnDuplicate($this->mediaGalleryTableName, $multiInsertData);
136-
$newMediaSelect = $this->connection->select()->from($this->mediaGalleryTableName, ['value_id', 'value'])
137-
->where('value IN (?)', $imageNames);
138-
if (array_keys($oldMediaValues)) {
139-
$newMediaSelect->where('value_id NOT IN (?)', array_keys($oldMediaValues));
145+
try {
146+
$mediaValueIdValueMap = [];
147+
$oldMediaValues = $this->connection->fetchCol(
148+
$this->connection->select()
149+
->from($this->mediaGalleryTableName, ['value_id'])
150+
->where('value IN (?)', $mediaGalleryValues)
151+
);
152+
$this->connection->insertOnDuplicate(
153+
$this->mediaGalleryTableName,
154+
$mediaGalleryValueData
155+
);
156+
$newMediaSelect = $this->connection->select()
157+
->from($this->mediaGalleryTableName, ['value_id', 'value'])
158+
->where('value IN (?)', $mediaGalleryValues);
159+
if ($oldMediaValues) {
160+
$newMediaSelect->where('value_id NOT IN (?)', $oldMediaValues);
161+
}
162+
$mediaValueIdValueMap = $this->connection->fetchPairs($newMediaSelect);
163+
$productIdMediaValueIdMap = $this->getProductIdMediaValueIdMap(
164+
$productMediaGalleryValueData,
165+
$mediaValueIdValueMap
166+
);
167+
$mediaGalleryValueToEntityData = $this->prepareMediaGalleryValueToEntityData(
168+
$mediaGalleryValueToEntityData,
169+
$productIdMediaValueIdMap
170+
);
171+
$this->connection->insertOnDuplicate(
172+
$this->mediaGalleryEntityToValueTableName,
173+
$mediaGalleryValueToEntityData,
174+
['value_id']
175+
);
176+
$mediaGalleryValueToStoreData = $this->prepareMediaGalleryValueData(
177+
$mediaGalleryValueToStoreData,
178+
$productIdMediaValueIdMap
179+
);
180+
$this->connection->insertOnDuplicate(
181+
$this->mediaGalleryValueTableName,
182+
$mediaGalleryValueToStoreData,
183+
['value_id', 'store_id', $productLinkIdField, 'label', 'position', 'disabled']
184+
);
185+
} catch (\Throwable $exception) {
186+
if ($mediaValueIdValueMap) {
187+
$this->connection->delete(
188+
$this->mediaGalleryTableName,
189+
$this->connection->quoteInto('value_id IN (?)', array_keys($mediaValueIdValueMap))
190+
);
191+
}
192+
throw $exception;
140193
}
141-
$newMediaValues = $this->connection->fetchAssoc($newMediaSelect);
142-
foreach ($mediaGalleryData as $storeId => $storeMediaGalleryData) {
143-
$this->processMediaPerStore((int)$storeId, $storeMediaGalleryData, $newMediaValues, $valueToProductId);
194+
}
195+
196+
/**
197+
* Get media values IDs per products IDs
198+
*
199+
* @param array $productMediaGalleryValueData
200+
* @param array $mediaValueIdValueMap
201+
* @return array
202+
*/
203+
private function getProductIdMediaValueIdMap(
204+
array $productMediaGalleryValueData,
205+
array $mediaValueIdValueMap
206+
): array {
207+
$productIdMediaValueIdMap = [];
208+
foreach ($productMediaGalleryValueData as $productId => $productMediaGalleryValues) {
209+
foreach ($productMediaGalleryValues as $productMediaGalleryValue) {
210+
foreach ($mediaValueIdValueMap as $valueId => $value) {
211+
if ($productMediaGalleryValue === $value) {
212+
$productIdMediaValueIdMap[$productId][$value] = $valueId;
213+
unset($mediaValueIdValueMap[$valueId]);
214+
break;
215+
}
216+
}
217+
}
218+
}
219+
return $productIdMediaValueIdMap;
220+
}
221+
222+
/**
223+
* Prepare media entity gallery value to entity data for insert
224+
*
225+
* @param array $mediaGalleryValueToEntityData
226+
* @param array $productIdMediaValueIdMap
227+
* @return array
228+
*/
229+
private function prepareMediaGalleryValueToEntityData(
230+
array $mediaGalleryValueToEntityData,
231+
array $productIdMediaValueIdMap
232+
): array {
233+
$productLinkIdField = $this->getProductEntityLinkField();
234+
foreach ($mediaGalleryValueToEntityData as $index => $data) {
235+
$productId = $data[$productLinkIdField];
236+
$value = $data['value'];
237+
$mediaGalleryValueToEntityData[$index]['value_id'] = $productIdMediaValueIdMap[$productId][$value];
238+
unset($mediaGalleryValueToEntityData[$index]['value']);
239+
}
240+
return $mediaGalleryValueToEntityData;
241+
}
242+
243+
/**
244+
* Prepare media entity gallery value data for insert
245+
*
246+
* @param array $mediaGalleryValueData
247+
* @param array $productIdMediaValueIdMap
248+
* @return array
249+
*/
250+
private function prepareMediaGalleryValueData(
251+
array $mediaGalleryValueData,
252+
array $productIdMediaValueIdMap
253+
): array {
254+
$productLinkIdField = $this->getProductEntityLinkField();
255+
$lastPositions = $this->getLastMediaPositionPerProduct(array_keys($productIdMediaValueIdMap));
256+
foreach ($mediaGalleryValueData as $index => $data) {
257+
$productId = $data[$productLinkIdField];
258+
$value = $data['value'];
259+
$position = $data['position'];
260+
$storeId = $data['store_id'];
261+
$mediaGalleryValueData[$index]['value_id'] = $productIdMediaValueIdMap[$productId][$value];
262+
$mediaGalleryValueData[$index]['position'] = $position + ($lastPositions[$storeId][$productId] ?? 0);
263+
unset($mediaGalleryValueData[$index]['value']);
144264
}
265+
return $mediaGalleryValueData;
145266
}
146267

147268
/**
@@ -289,13 +410,12 @@ private function initMediaGalleryResources()
289410
}
290411

291412
/**
292-
* Get the last media position for each product from the given list
413+
* Get the last media position for each product per store from the given list
293414
*
294-
* @param int $storeId
295415
* @param array $productIds
296416
* @return array
297417
*/
298-
private function getLastMediaPositionPerProduct(int $storeId, array $productIds): array
418+
private function getLastMediaPositionPerProduct(array $productIds): array
299419
{
300420
$result = [];
301421
if ($productIds) {
@@ -305,95 +425,25 @@ private function getLastMediaPositionPerProduct(int $storeId, array $productIds)
305425
$positions = $this->connection->fetchAll(
306426
$this->connection
307427
->select()
308-
->from($this->mediaGalleryValueTableName, [$productKeyName, 'position'])
428+
->from($this->mediaGalleryValueTableName, [$productKeyName, 'store_id', 'position'])
309429
->where("$productKeyName IN (?)", $productIds)
310-
->where('value_id is not null')
311-
->where('store_id = ?', $storeId)
312430
);
313-
// Make sure the result contains all product ids even if the product has no media files
314-
$result = array_fill_keys($productIds, 0);
315431
// Find the largest position for each product
316432
foreach ($positions as $record) {
317433
$productId = $record[$productKeyName];
318-
$result[$productId] = $result[$productId] < $record['position']
434+
$storeId = $record['store_id'];
435+
if (!isset($result[$storeId][$productId])) {
436+
$result[$storeId][$productId] = 0;
437+
}
438+
$result[$storeId][$productId] = $result[$storeId][$productId] < $record['position']
319439
? $record['position']
320-
: $result[$productId];
440+
: $result[$storeId][$productId];
321441
}
322442
}
323443

324444
return $result;
325445
}
326446

327-
/**
328-
* Save media gallery data per store.
329-
*
330-
* @param int $storeId
331-
* @param array $mediaGalleryData
332-
* @param array $newMediaValues
333-
* @param array $valueToProductId
334-
* @return void
335-
*/
336-
private function processMediaPerStore(
337-
int $storeId,
338-
array $mediaGalleryData,
339-
array $newMediaValues,
340-
array $valueToProductId
341-
) {
342-
$multiInsertData = [];
343-
$dataForSkinnyTable = [];
344-
$lastMediaPositionPerProduct = $this->getLastMediaPositionPerProduct(
345-
$storeId,
346-
array_unique(array_merge(...array_values($valueToProductId)))
347-
);
348-
349-
foreach ($mediaGalleryData as $mediaGalleryRows) {
350-
foreach ($mediaGalleryRows as $insertValue) {
351-
foreach ($newMediaValues as $valueId => $values) {
352-
if ($values['value'] == $insertValue['value']) {
353-
$insertValue['value_id'] = $valueId;
354-
$insertValue[$this->getProductEntityLinkField()]
355-
= array_shift($valueToProductId[$values['value']]);
356-
unset($newMediaValues[$valueId]);
357-
break;
358-
}
359-
}
360-
if (isset($insertValue['value_id'])) {
361-
$productId = $insertValue[$this->getProductEntityLinkField()];
362-
$valueArr = [
363-
'value_id' => $insertValue['value_id'],
364-
'store_id' => $storeId,
365-
$this->getProductEntityLinkField() => $productId,
366-
'label' => $insertValue['label'],
367-
'position' => $lastMediaPositionPerProduct[$productId] + $insertValue['position'],
368-
'disabled' => $insertValue['disabled'],
369-
];
370-
$multiInsertData[] = $valueArr;
371-
$dataForSkinnyTable[] = [
372-
'value_id' => $insertValue['value_id'],
373-
$this->getProductEntityLinkField() => $insertValue[$this->getProductEntityLinkField()],
374-
];
375-
}
376-
}
377-
}
378-
try {
379-
$this->connection->insertOnDuplicate(
380-
$this->mediaGalleryValueTableName,
381-
$multiInsertData,
382-
['value_id', 'store_id', $this->getProductEntityLinkField(), 'label', 'position', 'disabled']
383-
);
384-
$this->connection->insertOnDuplicate(
385-
$this->mediaGalleryEntityToValueTableName,
386-
$dataForSkinnyTable,
387-
['value_id']
388-
);
389-
} catch (\Exception $e) {
390-
$this->connection->delete(
391-
$this->mediaGalleryTableName,
392-
$this->connection->quoteInto('value_id IN (?)', $newMediaValues)
393-
);
394-
}
395-
}
396-
397447
/**
398448
* Get product entity link field.
399449
*

0 commit comments

Comments
 (0)