Skip to content

Commit 956f036

Browse files
authored
MDEE-976: Error in single item fails entire batch (#476)
1 parent 14c19f0 commit 956f036

File tree

12 files changed

+295
-116
lines changed

12 files changed

+295
-116
lines changed

CatalogDataExporter/Model/Provider/Category/Formatter/Formatter.php

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
namespace Magento\CatalogDataExporter\Model\Provider\Category\Formatter;
1919

20+
use Magento\DataExporter\Model\FailedItemsRegistry;
21+
use Magento\Framework\App\ObjectManager;
22+
2023
/**
2124
* Provider data formatter
2225
*/
@@ -25,24 +28,32 @@ class Formatter implements FormatterInterface
2528
/**
2629
* @var FormatterInterface[]
2730
*/
28-
private $formatters;
31+
private array $formatters;
32+
private ?FailedItemsRegistry $failedItemsRegistry;
2933

3034
/**
3135
* @param FormatterInterface[] $formatters
36+
* @param FailedItemsRegistry|null $failedRegistry
3237
*/
3338
public function __construct(
34-
array $formatters = []
39+
array $formatters = [],
40+
?FailedItemsRegistry $failedRegistry = null
3541
) {
3642
$this->formatters = $formatters;
43+
$this->failedItemsRegistry = $failedRegistry ?? ObjectManager::getInstance()->get(FailedItemsRegistry::class);
3744
}
3845

3946
/**
4047
* @inheritDoc
4148
*/
4249
public function format(array $row) : array
4350
{
44-
foreach ($this->formatters as $formatter) {
45-
$row = $formatter->format($row);
51+
try {
52+
foreach ($this->formatters as $formatter) {
53+
$row = $formatter->format($row);
54+
}
55+
} catch (\Throwable $e) {
56+
$this->failedItemsRegistry->addFailed($row, $e);
4657
}
4758

4859
return $row;

CatalogDataExporter/Model/Provider/EavAttributes/EavAttributesProvider.php

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
use Magento\DataExporter\Sql\FieldToPropertyNameConverter;
2323
use Magento\Framework\App\ResourceConnection;
2424
use Magento\DataExporter\Model\Logging\CommerceDataExportLoggerInterface as LoggerInterface;
25-
use Magento\Store\Model\Store;
2625

2726
/**
2827
* Eav attributes data provider
@@ -102,23 +101,10 @@ public function getEavAttributesData(array $entityIds, string $storeCode, array
102101
$attributes = $this->resourceConnection->getConnection()->fetchAll(
103102
$this->queryBuilder->build($entityIds, $attributeCodes, $storeCode)
104103
);
105-
$attributesPerEntity = $this->attributesDataConverter->convert($attributes);
106-
107-
// covers edge-case when export started before EAV attributes has been fulfilled
108-
if (in_array('name', $attributeCodes, true)) {
109-
foreach ($entityIds as $entityId) {
110-
if (!isset($attributesPerEntity[$entityId])) {
111-
$attributesPerEntity[$entityId] = [
112-
'id' => $entityId,
113-
'entity_id' => $entityId,
114-
'store_id' => Store::DEFAULT_STORE_ID,
115-
];
116-
}
117-
}
118-
}
104+
119105
return \array_map(function ($data) use ($attributeCodes) {
120106
return $this->formatEavAttributesArray($data, $attributeCodes);
121-
}, $attributesPerEntity);
107+
}, $this->attributesDataConverter->convert($attributes));
122108
} catch (\Throwable $exception) {
123109
$this->logger->error($exception->getMessage(), ['exception' => $exception]);
124110
throw new UnableRetrieveData('Unable to retrieve category eav attributes');

CatalogDataExporter/Model/Provider/EavAttributes/EntityEavAttributesResolver.php

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,6 @@ public function resolve(array $entitiesData, string $storeCode)
9191
$fullAttributesData = $this->eavAttributesProvider->getEavAttributesData($entityIds, $storeCode);
9292
}
9393

94-
$attributes = \array_replace($partialAttributesData, $fullAttributesData);
95-
if (empty($attributes)) {
96-
throw new UnableRetrieveData('Empty list of EAV attributes for products: ' . implode(',', $entityIds));
97-
}
98-
99-
return $attributes;
94+
return \array_replace($partialAttributesData, $fullAttributesData);
10095
}
10196
}

CatalogDataExporter/Model/Provider/Product/Displayable.php

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,23 @@
1616

1717
namespace Magento\CatalogDataExporter\Model\Provider\Product;
1818

19+
use Magento\DataExporter\Model\FailedItemsRegistry;
20+
1921
/**
2022
* Is product displayable data provider
2123
*/
2224
class Displayable
2325
{
26+
private FailedItemsRegistry $failedRegistry;
27+
28+
/**
29+
* @param FailedItemsRegistry $failedRegistry
30+
*/
31+
public function __construct(FailedItemsRegistry $failedRegistry)
32+
{
33+
$this->failedRegistry = $failedRegistry;
34+
}
35+
2436
/**
2537
* Get provider data
2638
*
@@ -32,14 +44,18 @@ public function get(array $values) : array
3244
$output = [];
3345

3446
foreach ($values as $value) {
35-
$output[] = [
36-
'productId' => $value['productId'],
37-
'storeViewCode' => $value['storeViewCode'],
38-
'displayable' => (
39-
$value['status'] === 'Enabled'
40-
&& in_array($value['visibility'], ['Catalog', 'Search', 'Catalog, Search'])
41-
)
42-
];
47+
try {
48+
$output[] = [
49+
'productId' => $value['productId'],
50+
'storeViewCode' => $value['storeViewCode'],
51+
'displayable' => (
52+
$value['status'] === 'Enabled'
53+
&& in_array($value['visibility'], ['Catalog', 'Search', 'Catalog, Search'])
54+
)
55+
];
56+
} catch (\Throwable $e) {
57+
$this->failedRegistry->addFailed($value, $e);
58+
}
4359
}
4460

4561
return $output;

CatalogDataExporter/Model/Provider/Product/Formatter/DateFormatter.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
2020

2121
/**
22-
* Class DateFormatter
22+
* Date formatter for product data
2323
*/
2424
class DateFormatter implements FormatterInterface
2525
{
@@ -33,7 +33,8 @@ class DateFormatter implements FormatterInterface
3333
/**
3434
* @param TimezoneInterface $timezone
3535
*/
36-
public function __construct(TimezoneInterface $timezone){
36+
public function __construct(TimezoneInterface $timezone)
37+
{
3738
$this->timezone = $timezone;
3839
}
3940

@@ -46,7 +47,6 @@ public function __construct(TimezoneInterface $timezone){
4647
public function format(array $row): array
4748
{
4849
$now = $this->timezone->date()->format('Y-m-d H:i:s');
49-
5050
if (isset($row['createdAt']) && $row['createdAt'] == self::INVALID_DATE) {
5151
$row['createdAt'] = $now;
5252
}

CatalogDataExporter/Model/Provider/Product/Formatter/Formatter.php

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
namespace Magento\CatalogDataExporter\Model\Provider\Product\Formatter;
1818

19+
use Magento\DataExporter\Model\FailedItemsRegistry;
20+
use Magento\Framework\App\ObjectManager;
21+
1922
/**
2023
* Provider data formatter
2124
*/
@@ -24,27 +27,35 @@ class Formatter implements FormatterInterface
2427
/**
2528
* @var FormatterInterface[]
2629
*/
27-
private $formatters;
30+
private array $formatters;
31+
private ?FailedItemsRegistry $failedItemsRegistry;
2832

2933
/**
30-
* Formatter constructor.
31-
*
3234
* @param FormatterInterface[] $formatters
35+
* @param FailedItemsRegistry|null $failedRegistry
3336
*/
3437
public function __construct(
35-
array $formatters = []
38+
array $formatters = [],
39+
?FailedItemsRegistry $failedRegistry = null
3640
) {
3741
$this->formatters = $formatters;
42+
$this->failedItemsRegistry = $failedRegistry ??
43+
ObjectManager::getInstance()->get(FailedItemsRegistry::class);
3844
}
3945

4046
/**
4147
* @inheritDoc
4248
*/
4349
public function format(array $row): array
4450
{
45-
foreach ($this->formatters as $formatter) {
46-
$row = $formatter->format($row);
51+
try {
52+
foreach ($this->formatters as $formatter) {
53+
$row = $formatter->format($row);
54+
}
55+
} catch (\Throwable $e) {
56+
$this->failedItemsRegistry->addFailed($row, $e);
4757
}
58+
4859
return $row;
4960
}
5061
}

CatalogDataExporter/Model/Provider/Product/ProductOptions/SelectableOptions.php

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
namespace Magento\CatalogDataExporter\Model\Provider\Product\ProductOptions;
1818

1919
use Magento\CatalogDataExporter\Model\Provider\Product\CustomOptions as CustomOptionsRepository;
20+
use Magento\DataExporter\Model\FailedItemsRegistry;
21+
use Magento\Framework\App\ObjectManager;
2022

2123
/**
2224
* Product options data provider, used for GraphQL resolver processing.
@@ -37,28 +39,25 @@ class SelectableOptions implements ProductOptionProviderInterface
3739
'multiple',
3840
];
3941

40-
/**
41-
* @var CustomOptionsRepository
42-
*/
43-
private $customOptionsRepository;
44-
45-
/**
46-
* @var CustomizableSelectedOptionValueUid
47-
*/
48-
private $optionValueUid;
42+
private CustomOptionsRepository $customOptionsRepository;
43+
private CustomizableSelectedOptionValueUid $optionValueUid;
44+
private FailedItemsRegistry $failedRegistry;
4945

5046
/**
5147
* SelectableOptions constructor.
5248
*
5349
* @param CustomOptionsRepository $customOptionsRepository
5450
* @param CustomizableSelectedOptionValueUid $optionValueUid
51+
* @param ?FailedItemsRegistry $failedRegistry
5552
*/
5653
public function __construct(
5754
CustomOptionsRepository $customOptionsRepository,
58-
CustomizableSelectedOptionValueUid $optionValueUid
55+
CustomizableSelectedOptionValueUid $optionValueUid,
56+
?FailedItemsRegistry $failedRegistry = null
5957
) {
6058
$this->customOptionsRepository = $customOptionsRepository;
6159
$this->optionValueUid = $optionValueUid;
60+
$this->failedRegistry = $failedRegistry ?? ObjectManager::getInstance()->get(FailedItemsRegistry::class);
6261
}
6362

6463
/**
@@ -108,15 +107,26 @@ public function get(array $values) : array
108107
}
109108
}
110109
foreach ($defaultFilteredProductOptions as $productId => $defaultOptions) {
111-
foreach ($defaultOptions as $defaultOption) {
112-
$key = $productId . $storeViewCode . $defaultOption['option_id'];
113-
$storeOption = $storeOptions[$key]['option'] ?? $defaultOption;
114-
$storeOption['values'] = $this->replaceDefaultOptionData(
115-
$defaultOption['values'],
116-
$storeViewOptionValues[$key] ?? []
117-
);
110+
try {
111+
foreach ($defaultOptions as $defaultOption) {
112+
$key = $productId . $storeViewCode . $defaultOption['option_id'];
113+
$storeOption = $storeOptions[$key]['option'] ?? $defaultOption;
114+
$storeOption['values'] = $this->replaceDefaultOptionData(
115+
$defaultOption['values'],
116+
$storeViewOptionValues[$key] ?? []
117+
);
118118

119-
$output[$key] = $this->formatOption((string)$productId, $storeViewCode, $storeOption);
119+
$output[$key] = $this->formatOption((string)$productId, $storeViewCode, $storeOption);
120+
}
121+
} catch (\Throwable $e) {
122+
$this->failedRegistry->addFailed(
123+
[
124+
'productId' => $productId,
125+
'storeViewCode' => $storeViewCode,
126+
'sku' => $defaultOption['product_sku'] ?? null,
127+
],
128+
$e
129+
);
120130
}
121131
}
122132
}
@@ -161,6 +171,8 @@ private function processOptionValues(
161171
}
162172

163173
/**
174+
* Format option data
175+
*
164176
* @param string $productId
165177
* @param string $storeViewCode
166178
* @param array $option
@@ -184,6 +196,8 @@ private function formatOption(string $productId, string $storeViewCode, array $o
184196
}
185197

186198
/**
199+
* Replace default option data with store option data
200+
*
187201
* @param array $defaultOption
188202
* @param array $storeOption
189203
* @return array

0 commit comments

Comments
 (0)