Skip to content

Commit cccd668

Browse files
authored
Merge pull request #5365 from magento-tsg/2.4.0-develop-pr17-2
[TSG] Fixes for 2.4 (pr17-2) (2.4.0-develop)
2 parents d6129f0 + 9f89122 commit cccd668

File tree

7 files changed

+265
-52
lines changed

7 files changed

+265
-52
lines changed

app/code/Magento/Catalog/Controller/Adminhtml/Product/Action/Attribute/Save.php

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@
77
namespace Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute;
88

99
use Magento\AsynchronousOperations\Api\Data\OperationInterface;
10+
use Magento\Catalog\Model\ProductFactory;
1011
use Magento\Eav\Model\Config;
1112
use Magento\Framework\App\Action\HttpPostActionInterface;
1213
use Magento\Backend\App\Action;
1314
use Magento\Framework\App\ObjectManager;
15+
use Magento\Framework\Exception\LocalizedException;
1416
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
1517

1618
/**
17-
* Class Save
19+
* Class responsible for saving product attributes.
1820
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1921
*/
2022
class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribute implements HttpPostActionInterface
@@ -59,6 +61,11 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribut
5961
*/
6062
private $eavConfig;
6163

64+
/**
65+
* @var ProductFactory
66+
*/
67+
private $productFactory;
68+
6269
/**
6370
* @param Action\Context $context
6471
* @param \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper
@@ -70,6 +77,7 @@ class Save extends \Magento\Catalog\Controller\Adminhtml\Product\Action\Attribut
7077
* @param int $bulkSize
7178
* @param TimezoneInterface $timezone
7279
* @param Config $eavConfig
80+
* @param ProductFactory $productFactory
7381
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
7482
*/
7583
public function __construct(
@@ -82,7 +90,8 @@ public function __construct(
8290
\Magento\Authorization\Model\UserContextInterface $userContext,
8391
int $bulkSize = 100,
8492
TimezoneInterface $timezone = null,
85-
Config $eavConfig = null
93+
Config $eavConfig = null,
94+
ProductFactory $productFactory = null
8695
) {
8796
parent::__construct($context, $attributeHelper);
8897
$this->bulkManagement = $bulkManagement;
@@ -95,6 +104,7 @@ public function __construct(
95104
->get(TimezoneInterface::class);
96105
$this->eavConfig = $eavConfig ?: ObjectManager::getInstance()
97106
->get(Config::class);
107+
$this->productFactory = $productFactory ?? ObjectManager::getInstance()->get(ProductFactory::class);
98108
}
99109

100110
/**
@@ -120,9 +130,10 @@ public function execute()
120130
$attributesData = $this->sanitizeProductAttributes($attributesData);
121131

122132
try {
133+
$this->validateProductAttributes($attributesData);
123134
$this->publish($attributesData, $websiteRemoveData, $websiteAddData, $storeId, $websiteId, $productIds);
124135
$this->messageManager->addSuccessMessage(__('Message is added to queue'));
125-
} catch (\Magento\Framework\Exception\LocalizedException $e) {
136+
} catch (LocalizedException $e) {
126137
$this->messageManager->addErrorMessage($e->getMessage());
127138
} catch (\Exception $e) {
128139
$this->messageManager->addExceptionMessage(
@@ -147,10 +158,12 @@ private function sanitizeProductAttributes($attributesData)
147158

148159
foreach ($attributesData as $attributeCode => $value) {
149160
$attribute = $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode);
161+
150162
if (!$attribute->getAttributeId()) {
151163
unset($attributesData[$attributeCode]);
152164
continue;
153165
}
166+
154167
if ($attribute->getBackendType() === 'datetime') {
155168
if (!empty($value)) {
156169
$filterInput = new \Zend_Filter_LocalizedToNormalized(['date_format' => $dateFormat]);
@@ -178,6 +191,25 @@ private function sanitizeProductAttributes($attributesData)
178191
return $attributesData;
179192
}
180193

194+
/**
195+
* Validate product attributes data.
196+
*
197+
* @param array $attributesData
198+
*
199+
* @return void
200+
* @throws LocalizedException
201+
*/
202+
private function validateProductAttributes(array $attributesData): void
203+
{
204+
$product = $this->productFactory->create();
205+
$product->setData($attributesData);
206+
207+
foreach (array_keys($attributesData) as $attributeCode) {
208+
$attribute = $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $attributeCode);
209+
$attribute->getBackend()->validate($product);
210+
}
211+
}
212+
181213
/**
182214
* Schedule new bulk
183215
*
@@ -187,7 +219,7 @@ private function sanitizeProductAttributes($attributesData)
187219
* @param int $storeId
188220
* @param int $websiteId
189221
* @param array $productIds
190-
* @throws \Magento\Framework\Exception\LocalizedException
222+
* @throws LocalizedException
191223
*
192224
* @return void
193225
*/
@@ -241,7 +273,7 @@ private function publish(
241273
$this->userContext->getUserId()
242274
);
243275
if (!$result) {
244-
throw new \Magento\Framework\Exception\LocalizedException(
276+
throw new LocalizedException(
245277
__('Something went wrong while processing the request.')
246278
);
247279
}

app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Save.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,8 @@ public function execute()
258258
unset($data['apply_to']);
259259
}
260260

261+
unset($data['entity_type_id']);
262+
261263
$model->addData($data);
262264

263265
if (!$attributeId) {

app/code/Magento/Eav/Model/Entity/Attribute.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,8 @@ public function beforeSave()
260260
);
261261
}
262262

263+
$this->validateEntityType();
264+
263265
$defaultValue = $this->getDefaultValue();
264266
$hasDefaultValue = (string)$defaultValue != '';
265267

@@ -535,4 +537,21 @@ public function __wakeup()
535537
$this->reservedAttributeList = $objectManager->get(\Magento\Catalog\Model\Product\ReservedAttributeList::class);
536538
$this->dateTimeFormatter = $objectManager->get(DateTimeFormatterInterface::class);
537539
}
540+
541+
/**
542+
* Entity type for existing attribute shouldn't be changed.
543+
*
544+
* @return void
545+
* @throws LocalizedException
546+
*/
547+
private function validateEntityType(): void
548+
{
549+
if ($this->getId() !== null) {
550+
$origEntityTypeId = $this->getOrigData('entity_type_id');
551+
552+
if (($origEntityTypeId !== null) && ((int)$this->getEntityTypeId() !== (int)$origEntityTypeId)) {
553+
throw new LocalizedException(__('Do not change entity type.'));
554+
}
555+
}
556+
}
538557
}

app/code/Magento/Eav/Model/ResourceModel/Entity/Attribute.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ public function loadByCode(AbstractModel $object, $entityTypeId, $code)
107107

108108
if ($data) {
109109
$object->setData($data);
110+
$object->setOrigData('entity_type_id', $object->getEntityTypeId());
110111
$this->_afterLoad($object);
111112
return true;
112113
}

dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/Action/AttributeTest.php

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,36 @@
55
*/
66
namespace Magento\Catalog\Controller\Adminhtml\Product\Action;
77

8+
use Magento\Backend\Model\Session;
9+
use Magento\Catalog\Block\Product\ListProduct;
10+
use Magento\Catalog\Helper\Product\Edit\Action\Attribute;
11+
use Magento\Catalog\Model\CategoryFactory;
812
use Magento\Catalog\Model\Product\Visibility;
13+
use Magento\Framework\Message\MessageInterface;
914
use Magento\Catalog\Model\ProductRepository;
1015
use Magento\Framework\App\Request\Http as HttpRequest;
16+
use Magento\Framework\UrlInterface;
1117
use Magento\TestFramework\Helper\Bootstrap;
18+
use Magento\TestFramework\MessageQueue\EnvironmentPreconditionException;
19+
use Magento\TestFramework\MessageQueue\PreconditionFailedException;
1220
use Magento\TestFramework\MessageQueue\PublisherConsumerController;
21+
use Magento\TestFramework\TestCase\AbstractBackendController;
1322

1423
/**
1524
* @magentoAppArea adminhtml
25+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1626
*/
17-
class AttributeTest extends \Magento\TestFramework\TestCase\AbstractBackendController
27+
class AttributeTest extends AbstractBackendController
1828
{
1929
/** @var PublisherConsumerController */
2030
private $publisherConsumerController;
2131
private $consumers = ['product_action_attribute.update'];
2232

2333
protected function setUp()
2434
{
25-
$this->publisherConsumerController = Bootstrap::getObjectManager()->create(
35+
parent::setUp();
36+
37+
$this->publisherConsumerController = $this->_objectManager->create(
2638
PublisherConsumerController::class,
2739
[
2840
'consumers' => $this->consumers,
@@ -34,15 +46,13 @@ protected function setUp()
3446

3547
try {
3648
$this->publisherConsumerController->startConsumers();
37-
} catch (\Magento\TestFramework\MessageQueue\EnvironmentPreconditionException $e) {
49+
} catch (EnvironmentPreconditionException $e) {
3850
$this->markTestSkipped($e->getMessage());
39-
} catch (\Magento\TestFramework\MessageQueue\PreconditionFailedException $e) {
51+
} catch (PreconditionFailedException $e) {
4052
$this->fail(
4153
$e->getMessage()
4254
);
4355
}
44-
45-
parent::setUp();
4656
}
4757

4858
protected function tearDown()
@@ -59,21 +69,19 @@ protected function tearDown()
5969
*/
6070
public function testSaveActionRedirectsSuccessfully()
6171
{
62-
$objectManager = Bootstrap::getObjectManager();
63-
64-
/** @var $session \Magento\Backend\Model\Session */
65-
$session = $objectManager->get(\Magento\Backend\Model\Session::class);
72+
/** @var $session Session */
73+
$session = $this->_objectManager->get(Session::class);
6674
$session->setProductIds([1]);
6775
$this->getRequest()->setMethod(HttpRequest::METHOD_POST);
6876

6977
$this->dispatch('backend/catalog/product_action_attribute/save/store/0');
7078

7179
$this->assertEquals(302, $this->getResponse()->getHttpResponseCode());
7280
/** @var \Magento\Backend\Model\UrlInterface $urlBuilder */
73-
$urlBuilder = $objectManager->get(\Magento\Framework\UrlInterface::class);
81+
$urlBuilder = $this->_objectManager->get(UrlInterface::class);
7482

75-
/** @var \Magento\Catalog\Helper\Product\Edit\Action\Attribute $attributeHelper */
76-
$attributeHelper = $objectManager->get(\Magento\Catalog\Helper\Product\Edit\Action\Attribute::class);
83+
/** @var Attribute $attributeHelper */
84+
$attributeHelper = $this->_objectManager->get(Attribute::class);
7785
$expectedUrl = $urlBuilder->getUrl(
7886
'catalog/product/index',
7987
['store' => $attributeHelper->getSelectedStoreId()]
@@ -98,32 +106,25 @@ public function testSaveActionRedirectsSuccessfully()
98106
*/
99107
public function testSaveActionChangeVisibility($attributes)
100108
{
101-
$objectManager = Bootstrap::getObjectManager();
102109
/** @var ProductRepository $repository */
103-
$repository = Bootstrap::getObjectManager()->create(
104-
ProductRepository::class
105-
);
110+
$repository = $this->_objectManager->create(ProductRepository::class);
106111
$product = $repository->get('simple');
107112
$product->setOrigData();
108113
$product->setVisibility(Visibility::VISIBILITY_NOT_VISIBLE);
109114
$product->save();
110115

111-
/** @var $session \Magento\Backend\Model\Session */
112-
$session = $objectManager->get(\Magento\Backend\Model\Session::class);
116+
/** @var $session Session */
117+
$session = $this->_objectManager->get(Session::class);
113118
$session->setProductIds([$product->getId()]);
114119
$this->getRequest()->setParam('attributes', $attributes);
115120
$this->getRequest()->setMethod(HttpRequest::METHOD_POST);
116121

117122
$this->dispatch('backend/catalog/product_action_attribute/save/store/0');
118123

119124
/** @var \Magento\Catalog\Model\Category $category */
120-
$categoryFactory = Bootstrap::getObjectManager()->get(
121-
\Magento\Catalog\Model\CategoryFactory::class
122-
);
123-
/** @var \Magento\Catalog\Block\Product\ListProduct $listProduct */
124-
$listProduct = Bootstrap::getObjectManager()->get(
125-
\Magento\Catalog\Block\Product\ListProduct::class
126-
);
125+
$categoryFactory = $this->_objectManager->get(CategoryFactory::class);
126+
/** @var ListProduct $listProduct */
127+
$listProduct = $this->_objectManager->get(ListProduct::class);
127128

128129
$this->publisherConsumerController->waitForAsynchronousResult(
129130
function () use ($repository) {
@@ -159,10 +160,8 @@ function () use ($repository) {
159160
*/
160161
public function testValidateActionWithMassUpdate($attributes)
161162
{
162-
$objectManager = Bootstrap::getObjectManager();
163-
164-
/** @var $session \Magento\Backend\Model\Session */
165-
$session = $objectManager->get(\Magento\Backend\Model\Session::class);
163+
/** @var $session Session */
164+
$session = $this->_objectManager->get(Session::class);
166165
$session->setProductIds([1, 2]);
167166

168167
$this->getRequest()->setParam('attributes', $attributes);
@@ -214,4 +213,34 @@ public function saveActionVisibilityAttrDataProvider()
214213
['arguments' => ['visibility' => Visibility::VISIBILITY_IN_CATALOG]]
215214
];
216215
}
216+
217+
/**
218+
* Assert that custom layout update can not be change for existing entity.
219+
*
220+
* @return void
221+
* @magentoDataFixture Magento/Catalog/_files/product_simple.php
222+
*/
223+
public function testSaveActionCantChangeCustomLayoutUpdate(): void
224+
{
225+
/** @var ProductRepository $repository */
226+
$repository = $this->_objectManager->get(ProductRepository::class);
227+
$product = $repository->get('simple');
228+
229+
$product->setOrigData('custom_layout_update', 'test');
230+
$product->setData('custom_layout_update', 'test');
231+
$product->save();
232+
/** @var $session Session */
233+
$session = $this->_objectManager->get(Session::class);
234+
$session->setProductIds([$product->getId()]);
235+
$this->getRequest()->setParam('attributes', ['custom_layout_update' => 'test2']);
236+
$this->getRequest()->setMethod(HttpRequest::METHOD_POST);
237+
238+
$this->dispatch('backend/catalog/product_action_attribute/save/store/0');
239+
240+
$this->assertSessionMessages(
241+
$this->equalTo(['Custom layout update text cannot be changed, only removed']),
242+
MessageInterface::TYPE_ERROR
243+
);
244+
$this->assertEquals('test', $product->getData('custom_layout_update'));
245+
}
217246
}

0 commit comments

Comments
 (0)