Skip to content

Commit 3ca6264

Browse files
author
mastiuhin-olexandr
committed
MC-40031: [UX] Configurations are not preserved on form reload when new configurable product creation fails
1 parent 81672a2 commit 3ca6264

File tree

3 files changed

+202
-1
lines changed

3 files changed

+202
-1
lines changed

app/code/Magento/Catalog/Controller/Adminhtml/Product/Validate.php

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
use Magento\Catalog\Controller\Adminhtml\Product;
1313
use Magento\Framework\App\ObjectManager;
1414
use Magento\Store\Model\StoreManagerInterface;
15+
use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException;
16+
use Magento\Catalog\Model\Product as ProductModel;
17+
use Magento\CatalogUrlRewrite\Model\Product\Validator as ProductUrlRewriteValidator;
1518

1619
/**
1720
* Product validate
@@ -57,6 +60,11 @@ class Validate extends Product implements HttpPostActionInterface, HttpGetAction
5760
*/
5861
private $storeManager;
5962

63+
/**
64+
* @var ProductUrlRewriteValidator
65+
*/
66+
private $productUrlRewriteValidator;
67+
6068
/**
6169
* @param Action\Context $context
6270
* @param Builder $productBuilder
@@ -65,6 +73,7 @@ class Validate extends Product implements HttpPostActionInterface, HttpGetAction
6573
* @param \Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory
6674
* @param \Magento\Framework\View\LayoutFactory $layoutFactory
6775
* @param \Magento\Catalog\Model\ProductFactory $productFactory
76+
* @param ProductUrlRewriteValidator $productUrlRewriteValidator
6877
*/
6978
public function __construct(
7079
\Magento\Backend\App\Action\Context $context,
@@ -73,14 +82,16 @@ public function __construct(
7382
\Magento\Catalog\Model\Product\Validator $productValidator,
7483
\Magento\Framework\Controller\Result\JsonFactory $resultJsonFactory,
7584
\Magento\Framework\View\LayoutFactory $layoutFactory,
76-
\Magento\Catalog\Model\ProductFactory $productFactory
85+
\Magento\Catalog\Model\ProductFactory $productFactory,
86+
ProductUrlRewriteValidator $productUrlRewriteValidator
7787
) {
7888
$this->_dateFilter = $dateFilter;
7989
$this->productValidator = $productValidator;
8090
parent::__construct($context, $productBuilder);
8191
$this->resultJsonFactory = $resultJsonFactory;
8292
$this->layoutFactory = $layoutFactory;
8393
$this->productFactory = $productFactory;
94+
$this->productUrlRewriteValidator = $productUrlRewriteValidator;
8495
}
8596

8697
/**
@@ -130,11 +141,18 @@ public function execute()
130141
$resource->getAttribute('news_from_date')->setMaxValue($product->getNewsToDate());
131142
$resource->getAttribute('custom_design_from')->setMaxValue($product->getCustomDesignTo());
132143

144+
$this->validateUrlKeyUniqueness($product);
133145
$this->productValidator->validate($product, $this->getRequest(), $response);
134146
} catch (\Magento\Eav\Model\Entity\Attribute\Exception $e) {
135147
$response->setError(true);
136148
$response->setAttribute($e->getAttributeCode());
137149
$response->setMessages([$e->getMessage()]);
150+
} catch (UrlAlreadyExistsException $e) {
151+
$this->messageManager->addExceptionMessage($e);
152+
$layout = $this->layoutFactory->create();
153+
$layout->initMessages();
154+
$response->setError(true);
155+
$response->setHtmlMessage($layout->getMessagesBlock()->getGroupedHtml());
138156
} catch (\Magento\Framework\Exception\LocalizedException $e) {
139157
$response->setError(true);
140158
$response->setMessages([$e->getMessage()]);
@@ -149,6 +167,32 @@ public function execute()
149167
return $this->resultJsonFactory->create()->setData($response);
150168
}
151169

170+
/**
171+
* Validates Url Key uniqueness.
172+
*
173+
* @param ProductModel $product
174+
* @throws UrlAlreadyExistsException
175+
*/
176+
private function validateUrlKeyUniqueness(ProductModel $product): void
177+
{
178+
$conflictingUrlRewrites = $this->productUrlRewriteValidator->findUrlKeyConflicts($product);
179+
180+
if ($conflictingUrlRewrites) {
181+
$data = [];
182+
183+
foreach ($conflictingUrlRewrites as $urlRewrite) {
184+
$data[$urlRewrite->getUrlRewriteId()] = $urlRewrite->toArray();
185+
}
186+
187+
throw new UrlAlreadyExistsException(
188+
__('URL key for specified store already exists.'),
189+
null,
190+
0,
191+
$data
192+
);
193+
}
194+
}
195+
152196
/**
153197
* @return StoreManagerInterface
154198
* @deprecated 101.0.0
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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+
namespace Magento\CatalogUrlRewrite\Model\Product;
9+
10+
use Magento\Catalog\Model\Product;
11+
use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator;
12+
use Magento\UrlRewrite\Model\UrlFinderInterface;
13+
use Magento\UrlRewrite\Service\V1\Data\UrlRewrite;
14+
use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator;
15+
use Magento\Store\Model\StoreManagerInterface;
16+
17+
/**
18+
* Url Rewrites Product validator.
19+
*/
20+
class Validator
21+
{
22+
/**
23+
* @var ProductUrlPathGenerator
24+
*/
25+
private $productUrlPathGenerator;
26+
27+
/**
28+
* @var UrlFinderInterface
29+
*/
30+
private $urlFinder;
31+
32+
/**
33+
* @var StoreManagerInterface
34+
*/
35+
private $storeManager;
36+
37+
/**
38+
* @param ProductUrlPathGenerator $productUrlPathGenerator
39+
* @param UrlFinderInterface $urlFinder
40+
* @param StoreManagerInterface $storeManager
41+
*/
42+
public function __construct(
43+
ProductUrlPathGenerator $productUrlPathGenerator,
44+
UrlFinderInterface $urlFinder,
45+
StoreManagerInterface $storeManager
46+
) {
47+
$this->productUrlPathGenerator = $productUrlPathGenerator;
48+
$this->urlFinder = $urlFinder;
49+
$this->storeManager = $storeManager;
50+
}
51+
52+
/**
53+
* Find Url Key conflicts of a product.
54+
*
55+
* @param Product $product
56+
* @return array Array of conflicting Url Keys.
57+
*/
58+
public function findUrlKeyConflicts(Product $product): array
59+
{
60+
if (!$product->getUrlKey()) {
61+
$urlKey = $this->productUrlPathGenerator->getUrlKey($product);
62+
$product->setUrlKey($urlKey);
63+
}
64+
65+
$stores = $this->storeManager->getStores();
66+
67+
$storeIdsToPathForSave = [];
68+
$searchData = [
69+
UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE,
70+
UrlRewrite::REQUEST_PATH => []
71+
];
72+
73+
foreach ($stores as $store) {
74+
if (!in_array($store->getWebsiteId(), $product->getWebsiteIds())) {
75+
continue;
76+
}
77+
78+
$urlPath = $this->productUrlPathGenerator->getUrlPathWithSuffix($product, $store->getId());
79+
$storeIdsToPathForSave[$store->getId()] = $urlPath;
80+
$searchData[UrlRewrite::REQUEST_PATH][] = $urlPath;
81+
}
82+
83+
$urlRewrites = $this->urlFinder->findAllByData($searchData);
84+
$conflicts = [];
85+
86+
foreach ($urlRewrites as $urlRewrite) {
87+
if (in_array($urlRewrite->getRequestPath(), $storeIdsToPathForSave)
88+
&& isset($storeIdsToPathForSave[$urlRewrite->getStoreId()])
89+
&& $storeIdsToPathForSave[$urlRewrite->getStoreId()] == $urlRewrite->getRequestPath()
90+
&& $product->getId() != $urlRewrite->getEntityId()) {
91+
$conflicts[] = $urlRewrite;
92+
}
93+
}
94+
95+
return $conflicts;
96+
}
97+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
namespace Magento\Catalog\Controller\Adminhtml\Product;
9+
10+
use Magento\TestFramework\TestCase\AbstractBackendController;
11+
use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException;
12+
use Magento\Framework\App\Request\Http as HttpRequest;
13+
use Magento\Catalog\Model\Product\Type;
14+
15+
/**
16+
* @magentoAppArea adminhtml
17+
*/
18+
class ValidateTest extends AbstractBackendController
19+
{
20+
/**
21+
* @magentoDataFixture Magento/Catalog/_files/product_simple.php
22+
*/
23+
public function testNotUniqueUrlKey()
24+
{
25+
$this->getRequest()
26+
->setMethod(HttpRequest::METHOD_POST);
27+
28+
$postData = [
29+
'product' => [
30+
'attribute_set_id' => '4',
31+
'status' => '1',
32+
'name' => 'Simple product',
33+
'sku' => 'simple',
34+
'type_id' => Type::TYPE_SIMPLE,
35+
'quantity_and_stock_status' => [
36+
'qty' => '10',
37+
'is_in_stock' => '1',
38+
],
39+
'website_ids' => [
40+
1 => '1',
41+
],
42+
'price' => '100',
43+
],
44+
];
45+
46+
$this->getRequest()
47+
->setPostValue($postData);
48+
$this->dispatch('backend/catalog/product/validate/');
49+
$responseBody = $this->getResponse()
50+
->getBody();
51+
52+
$message = __('The value specified in the URL Key field would generate a URL that already exists.');
53+
$additionalInfo = __('To resolve this conflict, you can either change the value of the URL Key field '
54+
. '(located in the Search Engine Optimization section) to a unique value, or change the Request Path fields'
55+
. ' in all locations listed below:');
56+
57+
$this->assertStringContainsString((string)$message, $responseBody);
58+
$this->assertStringContainsString((string)$additionalInfo, $responseBody);
59+
}
60+
}

0 commit comments

Comments
 (0)