1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\ConfigurableProduct\Model\Plugin;
use Magento\Catalog\Model\ProductFactory;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Framework\Exception\InputException;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\ConfigurableProduct\Api\Data\OptionInterface;
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
class ProductRepositorySave
{
/**
* @var ProductAttributeRepositoryInterface
*/
private $productAttributeRepository;
/**
* @var ProductFactory
*/
private $productFactory;
/**
* @param ProductAttributeRepositoryInterface $productAttributeRepository
* @param ProductFactory $productFactory
*/
public function __construct(
ProductAttributeRepositoryInterface $productAttributeRepository,
ProductFactory $productFactory
) {
$this->productAttributeRepository = $productAttributeRepository;
$this->productFactory = $productFactory;
}
/**
* Validate product links and reset configurable attributes to configurable product
*
* @param ProductRepositoryInterface $subject
* @param ProductInterface $result
* @param ProductInterface $product
* @param bool $saveOptions
* @return ProductInterface
* @throws CouldNotSaveException
* @throws InputException
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function afterSave(
ProductRepositoryInterface $subject,
ProductInterface $result,
ProductInterface $product,
$saveOptions = false
) {
if ($product->getTypeId() !== Configurable::TYPE_CODE) {
return $result;
}
$extensionAttributes = $result->getExtensionAttributes();
if ($extensionAttributes === null) {
return $result;
}
$configurableLinks = (array) $extensionAttributes->getConfigurableProductLinks();
$configurableOptions = (array) $extensionAttributes->getConfigurableProductOptions();
if (empty($configurableLinks) && empty($configurableOptions)) {
return $result;
}
$attributeCodes = [];
/** @var OptionInterface $configurableOption */
foreach ($configurableOptions as $configurableOption) {
$eavAttribute = $this->productAttributeRepository->get($configurableOption->getAttributeId());
$attributeCode = $eavAttribute->getAttributeCode();
$attributeCodes[] = $attributeCode;
}
$this->validateProductLinks($attributeCodes, $configurableLinks);
$result->getTypeInstance()->resetConfigurableAttributes($product);
return $result;
}
/**
* @param array $attributeCodes
* @param array $linkIds
* @return $this
* @throws InputException
*/
private function validateProductLinks(array $attributeCodes, array $linkIds)
{
$valueMap = [];
foreach ($linkIds as $productId) {
$variation = $this->productFactory->create()->load($productId);
$valueKey = '';
foreach ($attributeCodes as $attributeCode) {
if (!$variation->getData($attributeCode)) {
throw new InputException(
__('Product with id "%1" does not contain required attribute "%2".', $productId, $attributeCode)
);
}
$valueKey = $valueKey . $attributeCode . ':' . $variation->getData($attributeCode) . ';';
}
if (isset($valueMap[$valueKey])) {
throw new InputException(
__(
'Products "%1" and "%2" have the same set of attribute values.',
$productId,
$valueMap[$valueKey]
)
);
}
$valueMap[$valueKey] = $productId;
}
}
}