Skip to content

Commit 3bd07a1

Browse files
authored
ENGCOM-5836: Allow construction of products with custom_attributes in $data #24460
2 parents ebe81d8 + 94c40c9 commit 3bd07a1

File tree

4 files changed

+107
-17
lines changed

4 files changed

+107
-17
lines changed

app/code/Magento/Catalog/Model/Product.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,9 @@ public function __construct(
460460
$this->mediaGalleryEntryConverterPool = $mediaGalleryEntryConverterPool;
461461
$this->dataObjectHelper = $dataObjectHelper;
462462
$this->joinProcessor = $joinProcessor;
463+
$this->eavConfig = $config ?? ObjectManager::getInstance()->get(\Magento\Eav\Model\Config::class);
464+
$this->filterCustomAttribute = $filterCustomAttribute
465+
?? ObjectManager::getInstance()->get(FilterProductCustomAttribute::class);
463466
parent::__construct(
464467
$context,
465468
$registry,
@@ -470,9 +473,6 @@ public function __construct(
470473
$resourceCollection,
471474
$data
472475
);
473-
$this->eavConfig = $config ?? ObjectManager::getInstance()->get(\Magento\Eav\Model\Config::class);
474-
$this->filterCustomAttribute = $filterCustomAttribute
475-
?? ObjectManager::getInstance()->get(FilterProductCustomAttribute::class);
476476
}
477477

478478
/**
@@ -2160,6 +2160,7 @@ public function reset()
21602160
*/
21612161
public function getCacheIdTags()
21622162
{
2163+
// phpstan:ignore
21632164
$tags = parent::getCacheIdTags();
21642165
$affectedCategoryIds = $this->getAffectedCategoryIds();
21652166
if (!$affectedCategoryIds) {
@@ -2340,6 +2341,7 @@ public function isDisabled()
23402341
public function getImage()
23412342
{
23422343
$this->getTypeInstance()->setImageFromChildProduct($this);
2344+
// phpstan:ignore
23432345
return parent::getImage();
23442346
}
23452347

@@ -2403,6 +2405,7 @@ public function reloadPriceInfo()
24032405
}
24042406
}
24052407

2408+
// phpcs:disable PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames
24062409
/**
24072410
* Return Data Object data in array format.
24082411
*
@@ -2411,6 +2414,7 @@ public function reloadPriceInfo()
24112414
*/
24122415
public function __toArray()
24132416
{
2417+
// phpcs:enable PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames
24142418
$data = $this->_data;
24152419
$hasToArray = function ($model) {
24162420
return is_object($model) && method_exists($model, '__toArray') && is_callable([$model, '__toArray']);

app/code/Magento/Customer/Model/Address/AbstractAddress.php

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,13 @@ public function setData($key, $value = null)
281281
$key = $this->_implodeArrayField($key);
282282
} elseif (is_array($value) && $this->isAddressMultilineAttribute($key)) {
283283
$value = $this->_implodeArrayValues($value);
284+
} elseif (self::CUSTOM_ATTRIBUTES === $key && is_array($value)) {
285+
foreach ($value as &$attribute) {
286+
$attribute = is_array($attribute) ? $attribute : $attribute->__toArray();
287+
$attribute = $this->processCustomAttribute($attribute);
288+
}
284289
}
290+
285291
return parent::setData($key, $value);
286292
}
287293

@@ -637,8 +643,8 @@ public function unsRegion()
637643
* Is company required
638644
*
639645
* @return bool
640-
* @since 100.2.0
641646
* @throws \Magento\Framework\Exception\LocalizedException
647+
* @since 100.2.0
642648
*/
643649
protected function isCompanyRequired()
644650
{
@@ -649,8 +655,8 @@ protected function isCompanyRequired()
649655
* Is telephone required
650656
*
651657
* @return bool
652-
* @since 100.2.0
653658
* @throws \Magento\Framework\Exception\LocalizedException
659+
* @since 100.2.0
654660
*/
655661
protected function isTelephoneRequired()
656662
{
@@ -661,11 +667,30 @@ protected function isTelephoneRequired()
661667
* Is fax required
662668
*
663669
* @return bool
664-
* @since 100.2.0
665670
* @throws \Magento\Framework\Exception\LocalizedException
671+
* @since 100.2.0
666672
*/
667673
protected function isFaxRequired()
668674
{
669675
return ($this->_eavConfig->getAttribute('customer_address', 'fax')->getIsRequired());
670676
}
677+
678+
/**
679+
* Unify attribute format.
680+
*
681+
* @param array $attribute
682+
* @return array
683+
*/
684+
private function processCustomAttribute(array $attribute): array
685+
{
686+
if (isset($attribute['attribute_code']) &&
687+
isset($attribute['value']) &&
688+
is_array($attribute['value']) &&
689+
$this->isAddressMultilineAttribute($attribute['attribute_code'])
690+
) {
691+
$attribute['value'] = $this->_implodeArrayValues($attribute['value']);
692+
}
693+
694+
return $attribute;
695+
}
671696
}

dev/tests/integration/testsuite/Magento/Catalog/Model/ProductTest.php

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88

99
namespace Magento\Catalog\Model;
1010

11+
use Magento\Eav\Model\Config as EavConfig;
1112
use Magento\Catalog\Model\Product;
1213
use Magento\Framework\App\Filesystem\DirectoryList;
14+
use Magento\TestFramework\ObjectManager;
1315
use Magento\Catalog\Api\ProductRepositoryInterface;
1416
use Magento\Catalog\Model\Product\Attribute\Source\Status;
1517
use Magento\Framework\ObjectManagerInterface;
@@ -331,9 +333,9 @@ public function testIsSalable()
331333
$this->_model = $this->productRepository->get('simple');
332334

333335
// fixture
334-
$this->assertTrue((bool)$this->_model->isSalable());
335-
$this->assertTrue((bool)$this->_model->isSaleable());
336-
$this->assertTrue((bool)$this->_model->isAvailable());
336+
$this->assertTrue((bool) $this->_model->isSalable());
337+
$this->assertTrue((bool) $this->_model->isSaleable());
338+
$this->assertTrue((bool) $this->_model->isAvailable());
337339
$this->assertTrue($this->_model->isInStock());
338340
}
339341

@@ -350,9 +352,9 @@ public function testIsNotSalableWhenStatusDisabled()
350352
$this->_model = $this->productRepository->get('simple');
351353

352354
$this->_model->setStatus(0);
353-
$this->assertFalse((bool)$this->_model->isSalable());
354-
$this->assertFalse((bool)$this->_model->isSaleable());
355-
$this->assertFalse((bool)$this->_model->isAvailable());
355+
$this->assertFalse((bool) $this->_model->isSalable());
356+
$this->assertFalse((bool) $this->_model->isSaleable());
357+
$this->assertFalse((bool) $this->_model->isAvailable());
356358
$this->assertFalse($this->_model->isInStock());
357359
}
358360

@@ -631,7 +633,7 @@ public function testGetOptions()
631633
continue;
632634
}
633635
foreach ($option->getValues() as $value) {
634-
$this->assertEquals($expectedValue[$value->getSku()], (float)$value->getPrice());
636+
$this->assertEquals($expectedValue[$value->getSku()], (float) $value->getPrice());
635637
}
636638
}
637639
}
@@ -723,4 +725,40 @@ public function productWithBackordersDataProvider(): array
723725
[1, 1, true],
724726
];
725727
}
728+
729+
public function testConstructionWithCustomAttributesMapInData()
730+
{
731+
$data = [
732+
'custom_attributes' => [
733+
'tax_class_id' => '3',
734+
'category_ids' => '1,2'
735+
],
736+
];
737+
738+
/** @var Product $product */
739+
$product = ObjectManager::getInstance()->create(Product::class, ['data' => $data]);
740+
$this->assertSame($product->getCustomAttribute('tax_class_id')->getValue(), '3');
741+
$this->assertSame($product->getCustomAttribute('category_ids')->getValue(), '1,2');
742+
}
743+
744+
public function testConstructionWithCustomAttributesArrayInData()
745+
{
746+
$data = [
747+
'custom_attributes' => [
748+
[
749+
'attribute_code' => 'tax_class_id',
750+
'value' => '3'
751+
],
752+
[
753+
'attribute_code' => 'category_ids',
754+
'value' => '1,2'
755+
]
756+
],
757+
];
758+
759+
/** @var Product $product */
760+
$product = ObjectManager::getInstance()->create(Product::class, ['data' => $data]);
761+
$this->assertSame($product->getCustomAttribute('tax_class_id')->getValue(), '3');
762+
$this->assertSame($product->getCustomAttribute('category_ids')->getValue(), '1,2');
763+
}
726764
}

lib/internal/Magento/Framework/Model/AbstractExtensibleModel.php

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,28 @@ public function __construct(
7474
}
7575
}
7676

77+
/**
78+
* Convert the custom attributes array format to map format
79+
*
80+
* The method \Magento\Framework\Reflection\DataObjectProcessor::buildOutputDataArray generates a custom_attributes
81+
* array representation where each custom attribute is a sub-array with a `attribute_code and value key.
82+
* This method maps such an array to the plain code => value map format exprected by filterCustomAttributes
83+
*
84+
* @param array[] $customAttributesData
85+
* @return array
86+
*/
87+
private function flattenCustomAttributesArrayToMap(array $customAttributesData): array
88+
{
89+
return array_reduce(
90+
$customAttributesData,
91+
function (array $acc, array $customAttribute): array {
92+
$acc[$customAttribute['attribute_code']] = $customAttribute['value'];
93+
return $acc;
94+
},
95+
[]
96+
);
97+
}
98+
7799
/**
78100
* Verify custom attributes set on $data and unset if not a valid custom attribute
79101
*
@@ -85,9 +107,12 @@ protected function filterCustomAttributes($data)
85107
if (empty($data[self::CUSTOM_ATTRIBUTES])) {
86108
return $data;
87109
}
88-
$customAttributesCodes = $this->getCustomAttributesCodes();
110+
if (isset($data[self::CUSTOM_ATTRIBUTES][0])) {
111+
$data[self::CUSTOM_ATTRIBUTES] = $this->flattenCustomAttributesArrayToMap($data[self::CUSTOM_ATTRIBUTES]);
112+
}
113+
$customAttributesCodes = $this->getCustomAttributesCodes();
89114
$data[self::CUSTOM_ATTRIBUTES] = array_intersect_key(
90-
(array)$data[self::CUSTOM_ATTRIBUTES],
115+
(array) $data[self::CUSTOM_ATTRIBUTES],
91116
array_flip($customAttributesCodes)
92117
);
93118
foreach ($data[self::CUSTOM_ATTRIBUTES] as $code => $value) {
@@ -102,8 +127,6 @@ protected function filterCustomAttributes($data)
102127

103128
/**
104129
* Initialize customAttributes based on existing data
105-
*
106-
* @return $this
107130
*/
108131
protected function initializeCustomAttributes()
109132
{

0 commit comments

Comments
 (0)