Skip to content

Commit d1e1d02

Browse files
authored
ENGCOM-5683: #16832 - Reworked query in getAttributeRawValue so that it returns a store specific value even if there is no default value #23369
2 parents 0f803ee + 6093314 commit d1e1d02

File tree

4 files changed

+233
-14
lines changed

4 files changed

+233
-14
lines changed

app/code/Magento/Catalog/Model/ResourceModel/AbstractResource.php

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
/**
1616
* Catalog entity abstract model
1717
*
18+
* phpcs:disable Magento2.Classes.AbstractApi
1819
* @api
1920
*
2021
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -468,7 +469,7 @@ protected function _getOrigObject($object)
468469
*
469470
* @param AbstractAttribute $attribute
470471
* @param mixed $value New value of the attribute.
471-
* @param array &$origData
472+
* @param array $origData
472473
* @return bool
473474
*/
474475
protected function _canUpdateAttribute(AbstractAttribute $attribute, $value, array &$origData)
@@ -560,15 +561,19 @@ public function getAttributeRawValue($entityId, $attribute, $store)
560561
$store = (int) $store;
561562
if ($typedAttributes) {
562563
foreach ($typedAttributes as $table => $_attributes) {
564+
$defaultJoinCondition = [
565+
$connection->quoteInto('default_value.attribute_id IN (?)', array_keys($_attributes)),
566+
"default_value.{$this->getLinkField()} = e.{$this->getLinkField()}",
567+
'default_value.store_id = 0',
568+
];
569+
563570
$select = $connection->select()
564-
->from(['default_value' => $table], ['attribute_id'])
565-
->join(
566-
['e' => $this->getTable($this->getEntityTable())],
567-
'e.' . $this->getLinkField() . ' = ' . 'default_value.' . $this->getLinkField(),
568-
''
569-
)->where('default_value.attribute_id IN (?)', array_keys($_attributes))
570-
->where("e.entity_id = :entity_id")
571-
->where('default_value.store_id = ?', 0);
571+
->from(['e' => $this->getTable($this->getEntityTable())], [])
572+
->joinLeft(
573+
['default_value' => $table],
574+
implode(' AND ', $defaultJoinCondition),
575+
[]
576+
)->where("e.entity_id = :entity_id");
572577

573578
$bind = ['entity_id' => $entityId];
574579

@@ -578,6 +583,11 @@ public function getAttributeRawValue($entityId, $attribute, $store)
578583
'default_value.value',
579584
'store_value.value'
580585
);
586+
$attributeIdExpr = $connection->getCheckSql(
587+
'store_value.attribute_id IS NULL',
588+
'default_value.attribute_id',
589+
'store_value.attribute_id'
590+
);
581591
$joinCondition = [
582592
$connection->quoteInto('store_value.attribute_id IN (?)', array_keys($_attributes)),
583593
"store_value.{$this->getLinkField()} = e.{$this->getLinkField()}",
@@ -587,23 +597,28 @@ public function getAttributeRawValue($entityId, $attribute, $store)
587597
$select->joinLeft(
588598
['store_value' => $table],
589599
implode(' AND ', $joinCondition),
590-
['attr_value' => $valueExpr]
600+
['attribute_id' => $attributeIdExpr, 'attr_value' => $valueExpr]
591601
);
592602

593603
$bind['store_id'] = $store;
594604
} else {
595-
$select->columns(['attr_value' => 'value'], 'default_value');
605+
$select->columns(
606+
['attribute_id' => 'attribute_id', 'attr_value' => 'value'],
607+
'default_value'
608+
);
596609
}
597610

598611
$result = $connection->fetchPairs($select, $bind);
599612
foreach ($result as $attrId => $value) {
600-
$attrCode = $typedAttributes[$table][$attrId];
601-
$attributesData[$attrCode] = $value;
613+
if ($attrId !== '') {
614+
$attrCode = $typedAttributes[$table][$attrId];
615+
$attributesData[$attrCode] = $value;
616+
}
602617
}
603618
}
604619
}
605620

606-
if (is_array($attributesData) && sizeof($attributesData) == 1) {
621+
if (is_array($attributesData) && count($attributesData) == 1) {
607622
$attributesData = array_shift($attributesData);
608623
}
609624

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

100644100755
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,17 @@
99
use Magento\Framework\ObjectManagerInterface;
1010
use Magento\TestFramework\Helper\Bootstrap;
1111
use PHPUnit\Framework\TestCase;
12+
use Magento\Framework\Exception\NoSuchEntityException;
13+
use Magento\Framework\Exception\CouldNotSaveException;
14+
use Magento\Framework\Exception\InputException;
15+
use Magento\Framework\Exception\StateException;
1216

17+
/**
18+
* Tests product resource model
19+
*
20+
* @see \Magento\Catalog\Model\ResourceModel\Product
21+
* @see \Magento\Catalog\Model\ResourceModel\AbstractResource
22+
*/
1323
class ProductTest extends TestCase
1424
{
1525
/**
@@ -53,6 +63,87 @@ public function testGetAttributeRawValue()
5363
self::assertEquals($product->getName(), $actual);
5464
}
5565

66+
/**
67+
* @magentoAppArea adminhtml
68+
* @magentoDataFixture Magento/Catalog/_files/product_simple_with_custom_store_scope_attribute.php
69+
* @throws NoSuchEntityException
70+
* @throws CouldNotSaveException
71+
* @throws InputException
72+
* @throws StateException
73+
*/
74+
public function testGetAttributeRawValueGetDefault()
75+
{
76+
$product = $this->productRepository->get('simple_with_store_scoped_custom_attribute', true, 0, true);
77+
$product->setCustomAttribute('store_scoped_attribute_code', 'default_value');
78+
$this->productRepository->save($product);
79+
80+
$actual = $this->model->getAttributeRawValue($product->getId(), 'store_scoped_attribute_code', 1);
81+
$this->assertEquals('default_value', $actual);
82+
}
83+
84+
/**
85+
* @magentoAppArea adminhtml
86+
* @magentoDataFixture Magento/Catalog/_files/product_simple_with_custom_store_scope_attribute.php
87+
* @throws NoSuchEntityException
88+
* @throws CouldNotSaveException
89+
* @throws InputException
90+
* @throws StateException
91+
*/
92+
public function testGetAttributeRawValueGetStoreSpecificValueNoDefault()
93+
{
94+
$product = $this->productRepository->get('simple_with_store_scoped_custom_attribute', true, 0, true);
95+
$product->setCustomAttribute('store_scoped_attribute_code', null);
96+
$this->productRepository->save($product);
97+
98+
$product = $this->productRepository->get('simple_with_store_scoped_custom_attribute', true, 1, true);
99+
$product->setCustomAttribute('store_scoped_attribute_code', 'store_value');
100+
$this->productRepository->save($product);
101+
102+
$actual = $this->model->getAttributeRawValue($product->getId(), 'store_scoped_attribute_code', 1);
103+
$this->assertEquals('store_value', $actual);
104+
}
105+
106+
/**
107+
* @magentoAppArea adminhtml
108+
* @magentoDataFixture Magento/Catalog/_files/product_simple_with_custom_store_scope_attribute.php
109+
* @throws NoSuchEntityException
110+
* @throws CouldNotSaveException
111+
* @throws InputException
112+
* @throws StateException
113+
*/
114+
public function testGetAttributeRawValueGetStoreSpecificValueWithDefault()
115+
{
116+
$product = $this->productRepository->get('simple_with_store_scoped_custom_attribute', true, 0, true);
117+
$product->setCustomAttribute('store_scoped_attribute_code', 'default_value');
118+
$this->productRepository->save($product);
119+
120+
$product = $this->productRepository->get('simple_with_store_scoped_custom_attribute', true, 1, true);
121+
$product->setCustomAttribute('store_scoped_attribute_code', 'store_value');
122+
$this->productRepository->save($product);
123+
124+
$actual = $this->model->getAttributeRawValue($product->getId(), 'store_scoped_attribute_code', 1);
125+
$this->assertEquals('store_value', $actual);
126+
}
127+
128+
/**
129+
* @magentoAppArea adminhtml
130+
* @magentoDataFixture Magento/Catalog/_files/product_simple_with_custom_store_scope_attribute.php
131+
* @throws NoSuchEntityException
132+
* @throws CouldNotSaveException
133+
* @throws InputException
134+
* @throws StateException
135+
* @throws NoSuchEntityException
136+
*/
137+
public function testGetAttributeRawValueGetStoreValueFallbackToDefault()
138+
{
139+
$product = $this->productRepository->get('simple_with_store_scoped_custom_attribute', true, 0, true);
140+
$product->setCustomAttribute('store_scoped_attribute_code', 'default_value');
141+
$this->productRepository->save($product);
142+
143+
$actual = $this->model->getAttributeRawValue($product->getId(), 'store_scoped_attribute_code', 1);
144+
$this->assertEquals('default_value', $actual);
145+
}
146+
56147
/**
57148
* @magentoAppArea adminhtml
58149
* @magentoDataFixture Magento/Catalog/_files/product_special_price.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
use Magento\TestFramework\Helper\Bootstrap;
9+
use Magento\Catalog\Api\ProductRepositoryInterface;
10+
use Magento\Catalog\Model\ProductFactory;
11+
use Magento\Catalog\Api\Data\ProductAttributeInterface;
12+
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
13+
use Magento\Catalog\Setup\CategorySetup;
14+
use Magento\Eav\Model\Entity;
15+
use Magento\Catalog\Model\Product;
16+
use Magento\Catalog\Model\Product\Type;
17+
18+
/** @var \Magento\TestFramework\ObjectManager $objectManager */
19+
$objectManager = Bootstrap::getObjectManager();
20+
/** @var ProductRepositoryInterface $productRepository */
21+
$productRepository = $objectManager->get(ProductRepositoryInterface::class);
22+
/** @var ProductFactory $productFactory */
23+
$productFactory = $objectManager->get(ProductFactory::class);
24+
/** @var ProductAttributeRepositoryInterface $attributeRepository */
25+
$attributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class);
26+
27+
28+
/** @var $installer CategorySetup */
29+
$installer = $objectManager->create(CategorySetup::class);
30+
$entityModel = $objectManager->create(Entity::class);
31+
$attributeSetId = $installer->getAttributeSetId(Product::ENTITY, 'Default');
32+
$entityTypeId = $entityModel->setType(Product::ENTITY)
33+
->getTypeId();
34+
$groupId = $installer->getDefaultAttributeGroupId($entityTypeId, $attributeSetId);
35+
36+
/** @var ProductAttributeInterface $attribute */
37+
$attribute = $objectManager->create(ProductAttributeInterface::class);
38+
39+
$attribute->setAttributeCode('store_scoped_attribute_code')
40+
->setEntityTypeId($entityTypeId)
41+
->setIsVisible(true)
42+
->setFrontendInput('text')
43+
->setIsFilterable(1)
44+
->setIsUserDefined(1)
45+
->setUsedInProductListing(1)
46+
->setBackendType('varchar')
47+
->setIsUsedInGrid(1)
48+
->setIsVisibleInGrid(1)
49+
->setIsFilterableInGrid(1)
50+
->setFrontendLabel('nobody cares')
51+
->setAttributeGroupId($groupId)
52+
->setAttributeSetId(4);
53+
54+
$attributeRepository->save($attribute);
55+
56+
$product = $productFactory->create()
57+
->setTypeId(Type::TYPE_SIMPLE)
58+
->setAttributeSetId(4)
59+
->setName('Simple With Store Scoped Custom Attribute')
60+
->setSku('simple_with_store_scoped_custom_attribute')
61+
->setPrice(100)
62+
->setVisibility(1)
63+
->setStockData(
64+
[
65+
'use_config_manage_stock' => 1,
66+
'qty' => 100,
67+
'is_in_stock' => 1,
68+
]
69+
)
70+
->setStatus(1);
71+
$product->setCustomAttribute('store_scoped_attribute_code', 'default_value');
72+
$productRepository->save($product);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
use Magento\Framework\Registry;
9+
use Magento\TestFramework\Helper\Bootstrap;
10+
use Magento\Catalog\Api\ProductRepositoryInterface;
11+
use Magento\Framework\Exception\NoSuchEntityException;
12+
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
13+
14+
/** @var Magento\Framework\ObjectManagerInterface $objectManager */
15+
$objectManager = Bootstrap::getObjectManager();
16+
/** @var ProductRepositoryInterface $productRepository */
17+
$productRepository = $objectManager->get(ProductRepositoryInterface::class);
18+
/** @var ProductAttributeRepositoryInterface $attributeRepository */
19+
$attributeRepository = $objectManager->get(ProductAttributeRepositoryInterface::class);
20+
/** @var Registry $registry */
21+
$registry = $objectManager->get(Registry::class);
22+
23+
$registry->unregister('isSecureArea');
24+
$registry->register('isSecureArea', true);
25+
26+
try {
27+
/** @var \Magento\Catalog\Api\Data\ProductInterface $product */
28+
$product = $productRepository->get('simple_with_store_scoped_custom_attribute');
29+
$productRepository->delete($product);
30+
} catch (NoSuchEntityException $e) {
31+
}
32+
33+
try {
34+
/** @var \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute */
35+
$attribute = $attributeRepository->get('store_scoped_attribute_code');
36+
$attributeRepository->delete($attribute);
37+
} catch (NoSuchEntityException $e) {
38+
}
39+
40+
$registry->unregister('isSecureArea');
41+
$registry->register('isSecureArea', false);

0 commit comments

Comments
 (0)