Skip to content

Commit f1a3898

Browse files
authored
Merge branch '2.4-develop' into eav-graphql
2 parents 9f0ae06 + 8af9d33 commit f1a3898

File tree

33 files changed

+1539
-540
lines changed

33 files changed

+1539
-540
lines changed

app/code/Magento/Catalog/Test/Mftf/Test/AddNewProductAttributeInProductPageTest.xml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@
2121
<before>
2222
<!-- Login as admin -->
2323
<actionGroup ref="AdminLoginActionGroup" stepKey="login"/>
24-
2524
<!-- remove the Filter From the page-->
2625
<actionGroup ref="ClearFiltersAdminProductGridActionGroup" stepKey="clearFilterFromProductIndex"/>
27-
2826
<!--Create Category-->
2927
<createData entity="SimpleSubCategory" stepKey="createCategory"/>
3028

@@ -36,7 +34,8 @@
3634
<actionGroup ref="DeleteProductAttributeActionGroup" stepKey="deleteCreatedAttribute">
3735
<argument name="ProductAttribute" value="newProductAttribute"/>
3836
</actionGroup>
39-
37+
<actionGroup ref="AdminOpenProductIndexPageActionGroup" stepKey="navigateToProductListing"/>
38+
<actionGroup ref="ResetProductGridToDefaultViewActionGroup" stepKey="resetGridToDefaultKeywordSearch"/>
4039
<actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
4140
</after>
4241

@@ -109,6 +108,9 @@
109108
<wait stepKey="waitPostClickingCheck" time="5"/>
110109
<actionGroup ref="ToggleAdminProductGridColumnsDropdownActionGroup" stepKey="closeColumnsDropdown"/>
111110

111+
<actionGroup ref="FilterProductGridBySkuActionGroup" stepKey="filterProduct">
112+
<argument name="product" value="SimpleProduct"/>
113+
</actionGroup>
112114
<!-- Asserting the value of the created column -->
113115
<actionGroup ref="AssertAdminProductGridCellActionGroup" stepKey="seeCreatedAttributeColumn">
114116
<argument name="row" value="1"/>

app/code/Magento/Catalog/etc/adminhtml/di.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,4 +291,9 @@
291291
</argument>
292292
</arguments>
293293
</type>
294+
<type name="Magento\Catalog\Observer\ImageResizeAfterProductSave">
295+
<arguments>
296+
<argument name="imageResizeSchedulerFlag" xsi:type="boolean">true</argument>
297+
</arguments>
298+
</type>
294299
</config>

app/code/Magento/CatalogGraphQl/DataProvider/Product/LayeredNavigation/Formatter/LayerFormatter.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class LayerFormatter
2424
public function buildLayer($layerName, $itemsCount, $requestName, $position = null): array
2525
{
2626
return [
27-
'label' => $layerName,
27+
'label' => __($layerName),
2828
'count' => $itemsCount,
2929
'attribute_code' => $requestName,
3030
'position' => isset($position) ? (int)$position : null
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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\CatalogGraphQl\Model\Resolver\Products\Query;
9+
10+
use Magento\Catalog\Model\ResourceModel\Category\Collection;
11+
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory;
12+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
13+
use Magento\Framework\GraphQl\Query\Resolver\ArgumentsProcessorInterface;
14+
15+
/**
16+
* Category Path processor class for category url path argument
17+
*/
18+
class CategoryUrlPathArgsProcessor implements ArgumentsProcessorInterface
19+
{
20+
private const ID = 'category_id';
21+
22+
private const UID = 'category_uid';
23+
24+
private const URL_PATH = 'category_url_path';
25+
26+
/**
27+
* @var CollectionFactory
28+
*/
29+
private $collectionFactory;
30+
31+
/**
32+
* @param CollectionFactory $collectionFactory
33+
*/
34+
public function __construct(CollectionFactory $collectionFactory)
35+
{
36+
$this->collectionFactory = $collectionFactory;
37+
}
38+
39+
/**
40+
* Composite processor that loops through available processors for arguments that come from graphql input
41+
*
42+
* @param string $fieldName
43+
* @param array $args
44+
* @return array
45+
* @throws GraphQlInputException
46+
*/
47+
public function process(
48+
string $fieldName,
49+
array $args
50+
): array {
51+
$idFilter = $args['filter'][self::ID] ?? [];
52+
$uidFilter = $args['filter'][self::UID] ?? [];
53+
$pathFilter = $args['filter'][self::URL_PATH] ?? [];
54+
55+
if (!empty($pathFilter) && $fieldName === 'products') {
56+
if (!empty($idFilter)) {
57+
throw new GraphQlInputException(
58+
__('`%1` and `%2` can\'t be used at the same time.', [self::ID, self::URL_PATH])
59+
);
60+
} elseif (!empty($uidFilter)) {
61+
throw new GraphQlInputException(
62+
__('`%1` and `%2` can\'t be used at the same time.', [self::UID, self::URL_PATH])
63+
);
64+
}
65+
66+
/** @var Collection $collection */
67+
$collection = $this->collectionFactory->create();
68+
$collection->addAttributeToSelect('entity_id');
69+
$collection->addAttributeToFilter('url_path', $pathFilter);
70+
71+
if ($collection->count() === 0) {
72+
throw new GraphQlInputException(
73+
__('No category with the provided `%1` was found', [self::URL_PATH])
74+
);
75+
} elseif ($collection->count() === 1) {
76+
$category = $collection->getFirstItem();
77+
$args['filter'][self::ID]['eq'] = $category->getId();
78+
} else {
79+
$categoryIds = [];
80+
foreach ($collection as $category) {
81+
$categoryIds[] = $category->getId();
82+
}
83+
$args['filter'][self::ID]['in'] = $categoryIds;
84+
}
85+
86+
unset($args['filter'][self::URL_PATH]);
87+
}
88+
return $args;
89+
}
90+
}

app/code/Magento/CatalogGraphQl/etc/di.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
<type name="Magento\Framework\GraphQl\Query\Resolver\ArgumentsCompositeProcessor">
7979
<arguments>
8080
<argument name="processors" xsi:type="array">
81+
<item name="category_url_path" xsi:type="object">Magento\CatalogGraphQl\Model\Resolver\Products\Query\CategoryUrlPathArgsProcessor</item>
8182
<item name="category_uid" xsi:type="object">Magento\CatalogGraphQl\Model\Resolver\Products\Query\CategoryUidArgsProcessor</item>
8283
<item name="category_uids" xsi:type="object">Magento\CatalogGraphQl\Model\Category\CategoryUidsArgsProcessor</item>
8384
<item name="parent_category_uids" xsi:type="object">Magento\CatalogGraphQl\Model\Category\ParentCategoryUidsArgsProcessor</item>

app/code/Magento/CatalogGraphQl/etc/schema.graphqls

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ type CategoryProducts @doc(description: "Contains details about the products ass
344344
input ProductAttributeFilterInput @doc(description: "Defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") {
345345
category_id: FilterEqualTypeInput @deprecated(reason: "Use `category_uid` instead.") @doc(description: "Deprecated: use `category_uid` to filter product by category ID.")
346346
category_uid: FilterEqualTypeInput @doc(description: "Filter product by the unique ID for a `CategoryInterface` object.")
347+
category_url_path: FilterEqualTypeInput @doc(description: "Filter product by category URL path.")
347348
}
348349

349350
input CategoryFilterInput @doc(description: "Defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.")

app/code/Magento/CatalogUrlRewrite/Model/ProductScopeRewriteGenerator.php

Lines changed: 86 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,18 @@ public function generateForGlobalScope($productCategories, Product $product, $ro
161161
Product::ENTITY
162162
)) {
163163
$mergeDataProvider->merge(
164-
$this->generateForSpecificStoreView($id, $productCategories, $product, $rootCategoryId)
164+
$this->generateForSpecificStoreView($id, $productCategories, $product, $rootCategoryId, true)
165165
);
166166
} else {
167167
$scopedProduct = $this->productRepository->getById($productId, false, $id);
168168
$mergeDataProvider->merge(
169-
$this->generateForSpecificStoreView($id, $productCategories, $scopedProduct, $rootCategoryId)
169+
$this->generateForSpecificStoreView(
170+
$id,
171+
$productCategories,
172+
$scopedProduct,
173+
$rootCategoryId,
174+
true
175+
)
170176
);
171177
}
172178
}
@@ -182,12 +188,20 @@ public function generateForGlobalScope($productCategories, Product $product, $ro
182188
* @param \Magento\Framework\Data\Collection|Category[] $productCategories
183189
* @param \Magento\Catalog\Model\Product $product
184190
* @param int|null $rootCategoryId
191+
* @param bool $isGlobalScope
185192
* @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[]
193+
* @throws NoSuchEntityException
186194
*/
187-
public function generateForSpecificStoreView($storeId, $productCategories, Product $product, $rootCategoryId = null)
188-
{
195+
public function generateForSpecificStoreView(
196+
$storeId,
197+
$productCategories,
198+
Product $product,
199+
$rootCategoryId = null,
200+
bool $isGlobalScope = false
201+
) {
189202
$mergeDataProvider = clone $this->mergeDataProviderPrototype;
190203
$categories = [];
204+
191205
foreach ($productCategories as $category) {
192206
if (!$this->isCategoryProperForGenerating($category, $storeId)) {
193207
continue;
@@ -196,35 +210,29 @@ public function generateForSpecificStoreView($storeId, $productCategories, Produ
196210
$categories[] = $this->getCategoryWithOverriddenUrlKey($storeId, $category);
197211
}
198212

199-
$productCategories = $this->objectRegistryFactory->create(['entities' => $categories]);
200-
201213
$mergeDataProvider->merge(
202214
$this->canonicalUrlRewriteGenerator->generate($storeId, $product)
203215
);
204216

205-
if ($this->isCategoryRewritesEnabled()) {
206-
$mergeDataProvider->merge(
207-
$this->categoriesUrlRewriteGenerator->generate($storeId, $product, $productCategories)
208-
);
217+
$productCategories = $this->objectRegistryFactory->create(['entities' => $categories]);
218+
219+
if ($isGlobalScope) {
220+
$generatedUrls = $this->generateCategoryUrls((int) $storeId, $product, $productCategories);
221+
} else {
222+
$generatedUrls = $this->generateCategoryUrlsInStoreGroup((int) $storeId, $product, $productCategories);
209223
}
210224

225+
$mergeDataProvider->merge(array_merge(...$generatedUrls));
211226
$mergeDataProvider->merge(
212-
$this->currentUrlRewritesRegenerator->generate(
227+
$this->currentUrlRewritesRegenerator->generateAnchor(
213228
$storeId,
214229
$product,
215230
$productCategories,
216231
$rootCategoryId
217232
)
218233
);
219-
220-
if ($this->isCategoryRewritesEnabled()) {
221-
$mergeDataProvider->merge(
222-
$this->anchorUrlRewriteGenerator->generate($storeId, $product, $productCategories)
223-
);
224-
}
225-
226234
$mergeDataProvider->merge(
227-
$this->currentUrlRewritesRegenerator->generateAnchor(
235+
$this->currentUrlRewritesRegenerator->generate(
228236
$storeId,
229237
$product,
230238
$productCategories,
@@ -252,6 +260,65 @@ public function isCategoryProperForGenerating(Category $category, $storeId)
252260
return false;
253261
}
254262

263+
/**
264+
* Generate category URLs for the whole store group.
265+
*
266+
* @param int $storeId
267+
* @param Product $product
268+
* @param ObjectRegistry $productCategories
269+
*
270+
* @return array
271+
* @throws NoSuchEntityException
272+
*/
273+
private function generateCategoryUrlsInStoreGroup(
274+
int $storeId,
275+
Product $product,
276+
ObjectRegistry $productCategories
277+
): array {
278+
$currentStore = $this->storeManager->getStore($storeId);
279+
$currentGroupId = $currentStore->getStoreGroupId();
280+
$storeList = $this->storeManager->getStores();
281+
$generatedUrls = [];
282+
283+
foreach ($storeList as $store) {
284+
if ($store->getStoreGroupId() === $currentGroupId && $this->isCategoryRewritesEnabled()) {
285+
$groupStoreId = (int) $store->getId();
286+
$generatedUrls[] = $this->generateCategoryUrls(
287+
$groupStoreId,
288+
$product,
289+
$productCategories
290+
);
291+
}
292+
}
293+
294+
return array_merge(...$generatedUrls);
295+
}
296+
297+
/**
298+
* Generate category URLs.
299+
*
300+
* @param int $storeId
301+
* @param Product $product
302+
* @param ObjectRegistry $categories
303+
*
304+
* @return array
305+
*/
306+
private function generateCategoryUrls(int $storeId, Product $product, ObjectRegistry $categories): array
307+
{
308+
$generatedUrls[] = $this->categoriesUrlRewriteGenerator->generate(
309+
$storeId,
310+
$product,
311+
$categories
312+
);
313+
$generatedUrls[] = $this->anchorUrlRewriteGenerator->generate(
314+
$storeId,
315+
$product,
316+
$categories
317+
);
318+
319+
return $generatedUrls;
320+
}
321+
255322
/**
256323
* Check if URL key has been changed
257324
*

app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductScopeRewriteGeneratorTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ public function testGenerationForGlobalScope()
166166
$product = $this->createMock(Product::class);
167167
$product->expects($this->any())->method('getStoreId')->willReturn(null);
168168
$product->expects($this->any())->method('getStoreIds')->willReturn([1]);
169+
$store = $this->getMockBuilder(Store::class)
170+
->disableOriginalConstructor()
171+
->getMock();
172+
$store->expects($this->any())->method('getStoreGroupId')->willReturn(1);
173+
$this->storeManager->expects($this->any())->method('getStores')->willReturn([$store]);
169174
$this->storeViewService->expects($this->once())->method('doesEntityHaveOverriddenUrlKeyForStore')
170175
->willReturn(true);
171176
$this->initObjectRegistryFactory([]);
@@ -211,6 +216,11 @@ public function testGenerationForSpecificStore()
211216
$product = $this->createMock(Product::class);
212217
$product->expects($this->any())->method('getStoreId')->willReturn(1);
213218
$product->expects($this->never())->method('getStoreIds');
219+
$store = $this->getMockBuilder(Store::class)
220+
->disableOriginalConstructor()
221+
->getMock();
222+
$store->expects($this->any())->method('getStoreGroupId')->willReturn(1);
223+
$this->storeManager->expects($this->any())->method('getStores')->willReturn([$store]);
214224
$this->categoryMock->expects($this->any())->method('getParentIds')
215225
->willReturn(['root-id', $storeRootCategoryId]);
216226
$this->categoryMock->expects($this->any())->method('getId')->willReturn($category_id);
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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\CmsGraphQl\Model\Resolver\Block;
9+
10+
use Magento\Cms\Api\Data\BlockInterface;
11+
use Magento\Cms\Model\Block;
12+
use Magento\Framework\GraphQl\Query\Resolver\IdentityInterface;
13+
14+
class ResolverCacheIdentity implements IdentityInterface
15+
{
16+
/**
17+
* @var string
18+
*/
19+
private $cacheTag = Block::CACHE_TAG;
20+
21+
/**
22+
* Get block identities from resolved data
23+
*
24+
* @param array $resolvedData
25+
* @return string[]
26+
*/
27+
public function getIdentities(array $resolvedData): array
28+
{
29+
$ids = [];
30+
$items = $resolvedData['items'] ?? [];
31+
foreach ($items as $item) {
32+
if (is_array($item) && !empty($item[BlockInterface::BLOCK_ID])) {
33+
$ids[] = sprintf('%s_%s', $this->cacheTag, $item[BlockInterface::BLOCK_ID]);
34+
$ids[] = sprintf('%s_%s', $this->cacheTag, $item[BlockInterface::IDENTIFIER]);
35+
}
36+
}
37+
38+
return $ids;
39+
}
40+
}

app/code/Magento/CmsGraphQl/etc/di.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
<item name="Magento\Cms\Api\Data\PageInterface" xsi:type="string">
1313
Magento\Cms\Api\Data\PageInterface
1414
</item>
15+
<item name="Magento\Cms\Api\Data\BlockInterface" xsi:type="string">
16+
Magento\Cms\Api\Data\BlockInterface
17+
</item>
1518
</argument>
1619
</arguments>
1720
</type>

app/code/Magento/CmsGraphQl/etc/graphql/di.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
<item name="Magento\CmsGraphQl\Model\Resolver\Page" xsi:type="string">
2525
Magento\CmsGraphQl\Model\Resolver\Page\ResolverCacheIdentity
2626
</item>
27+
<item name="Magento\CmsGraphQl\Model\Resolver\Blocks" xsi:type="string">
28+
Magento\CmsGraphQl\Model\Resolver\Block\ResolverCacheIdentity
29+
</item>
2730
</argument>
2831
</arguments>
2932
</type>

0 commit comments

Comments
 (0)