Skip to content

Commit 9380470

Browse files
committed
Merge branch 'ACP2E-3419' of https://github.com/adobe-commerce-tier-4/magento2ce into PR-VK-2024-11-27-CE
2 parents 996cd5e + f510a5a commit 9380470

File tree

3 files changed

+224
-17
lines changed

3 files changed

+224
-17
lines changed

app/code/Magento/RelatedProductGraphQl/Model/Resolver/Batch/AbstractLikedProducts.php

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2019 Adobe
4+
* All Rights Reserved.
55
*/
66
declare(strict_types=1);
77

88
namespace Magento\RelatedProductGraphQl\Model\Resolver\Batch;
99

10+
use Magento\Catalog\Api\Data\ProductInterface;
1011
use Magento\CatalogGraphQl\Model\Resolver\Product\ProductFieldsSelector;
12+
use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product as ProductDataProvider;
13+
use Magento\Framework\Api\SearchCriteriaBuilder;
14+
use Magento\Framework\App\ObjectManager;
1115
use Magento\Framework\Exception\LocalizedException;
1216
use Magento\Framework\GraphQl\Config\Element\Field;
17+
use Magento\Framework\GraphQl\Query\Resolver\BatchRequestItemInterface;
1318
use Magento\Framework\GraphQl\Query\Resolver\BatchResolverInterface;
1419
use Magento\Framework\GraphQl\Query\Resolver\BatchResponse;
1520
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
1621
use Magento\RelatedProductGraphQl\Model\DataProvider\RelatedProductDataProvider;
17-
use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Product as ProductDataProvider;
18-
use Magento\Framework\Api\SearchCriteriaBuilder;
22+
use Magento\RelatedProductGraphQl\Model\ResourceModel\RelatedProductsByStoreId;
1923

2024
/**
2125
* Resolve linked product lists.
@@ -42,22 +46,31 @@ abstract class AbstractLikedProducts implements BatchResolverInterface
4246
*/
4347
private $searchCriteriaBuilder;
4448

49+
/**
50+
* @var RelatedProductsByStoreId
51+
*/
52+
private $relatedProductsByStoreId;
53+
4554
/**
4655
* @param ProductFieldsSelector $productFieldsSelector
4756
* @param RelatedProductDataProvider $relatedProductDataProvider
4857
* @param ProductDataProvider $productDataProvider
4958
* @param SearchCriteriaBuilder $searchCriteriaBuilder
59+
* @param RelatedProductsByStoreId|null $relatedProductsByStoreId
5060
*/
5161
public function __construct(
5262
ProductFieldsSelector $productFieldsSelector,
5363
RelatedProductDataProvider $relatedProductDataProvider,
5464
ProductDataProvider $productDataProvider,
55-
SearchCriteriaBuilder $searchCriteriaBuilder
65+
SearchCriteriaBuilder $searchCriteriaBuilder,
66+
?RelatedProductsByStoreId $relatedProductsByStoreId = null
5667
) {
5768
$this->productFieldsSelector = $productFieldsSelector;
5869
$this->relatedProductDataProvider = $relatedProductDataProvider;
5970
$this->productDataProvider = $productDataProvider;
6071
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
72+
$this->relatedProductsByStoreId = $relatedProductsByStoreId ??
73+
ObjectManager::getInstance()->get(RelatedProductsByStoreId::class);
6174
}
6275

6376
/**
@@ -77,29 +90,39 @@ abstract protected function getLinkType(): int;
7790
/**
7891
* Find related products.
7992
*
80-
* @param \Magento\Catalog\Api\Data\ProductInterface[] $products
93+
* @param ProductInterface[] $products
8194
* @param string[] $loadAttributes
8295
* @param int $linkType
83-
* @return \Magento\Catalog\Api\Data\ProductInterface[][]
96+
* @param string $websiteId
97+
* @return ProductInterface[][]
98+
* @throws LocalizedException
8499
*/
85-
private function findRelations(array $products, array $loadAttributes, int $linkType): array
86-
{
100+
private function findRelations(
101+
array $products,
102+
array $loadAttributes,
103+
int $linkType,
104+
string $websiteId
105+
): array {
87106
//Loading relations
88107
$relations = $this->relatedProductDataProvider->getRelations($products, $linkType);
89108
if (!$relations) {
90109
return [];
91110
}
92-
$relatedIds = array_unique(array_merge([], ...array_values($relations)));
111+
//get related product ids by website id
112+
$relatedIds = $this->relatedProductsByStoreId->execute(
113+
array_unique(array_merge([], ...array_values($relations))),
114+
$websiteId
115+
);
93116
//Loading products data.
94117
$this->searchCriteriaBuilder->addFilter('entity_id', $relatedIds, 'in');
95118
$relatedSearchResult = $this->productDataProvider->getList(
96119
$this->searchCriteriaBuilder->create(),
97120
$loadAttributes
98121
);
99122
//Filling related products map.
100-
/** @var \Magento\Catalog\Api\Data\ProductInterface[] $relatedProducts */
123+
/** @var ProductInterface[] $relatedProducts */
101124
$relatedProducts = [];
102-
/** @var \Magento\Catalog\Api\Data\ProductInterface $item */
125+
/** @var ProductInterface $item */
103126
foreach ($relatedSearchResult->getItems() as $item) {
104127
$relatedProducts[$item->getId()] = $item;
105128
}
@@ -127,10 +150,10 @@ function ($id) use ($relatedProducts) {
127150
*/
128151
public function resolve(ContextInterface $context, Field $field, array $requests): BatchResponse
129152
{
130-
/** @var \Magento\Catalog\Api\Data\ProductInterface[] $products */
153+
/** @var ProductInterface[] $products */
131154
$products = [];
132155
$fields = [];
133-
/** @var \Magento\Framework\GraphQl\Query\Resolver\BatchRequestItemInterface $request */
156+
/** @var BatchRequestItemInterface $request */
134157
foreach ($requests as $request) {
135158
//Gathering fields and relations to load.
136159
if (empty($request->getValue()['model'])) {
@@ -141,14 +164,16 @@ public function resolve(ContextInterface $context, Field $field, array $requests
141164
}
142165
$fields = array_unique(array_merge([], ...$fields));
143166

167+
$store = $context->getExtensionAttributes()->getStore();
168+
$websiteId = $store->getWebsiteId();
144169
//Finding relations.
145-
$related = $this->findRelations($products, $fields, $this->getLinkType());
170+
$related = $this->findRelations($products, $fields, $this->getLinkType(), (string) $websiteId);
146171

147172
//Matching requests with responses.
148173
$response = new BatchResponse();
149-
/** @var \Magento\Framework\GraphQl\Query\Resolver\BatchRequestItemInterface $request */
174+
/** @var BatchRequestItemInterface $request */
150175
foreach ($requests as $request) {
151-
/** @var \Magento\Catalog\Api\Data\ProductInterface $product */
176+
/** @var ProductInterface $product */
152177
$product = $request->getValue()['model'];
153178
$result = [];
154179
if (array_key_exists($product->getId(), $related)) {
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\RelatedProductGraphQl\Model\ResourceModel;
9+
10+
use Magento\Catalog\Model\ResourceModel\Product\Link;
11+
use Magento\Framework\App\ResourceConnection;
12+
use Magento\Framework\DB\Adapter\AdapterInterface;
13+
use Magento\Framework\Exception\LocalizedException;
14+
15+
/**
16+
* Processing db operations for retrieving related products by storeId
17+
*/
18+
class RelatedProductsByStoreId
19+
{
20+
/**
21+
* @var Link
22+
*/
23+
private $productLink;
24+
25+
/**
26+
* @var AdapterInterface
27+
*/
28+
private $connection;
29+
30+
/**
31+
* @param Link $productLink
32+
* @param ResourceConnection $resource
33+
*/
34+
public function __construct(
35+
Link $productLink,
36+
ResourceConnection $resource,
37+
) {
38+
$this->productLink = $productLink;
39+
$this->connection = $resource->getConnection();
40+
}
41+
42+
/**
43+
* Get Product Models by storeId
44+
*
45+
* @param array $linkedProductIds
46+
* @param string $websiteId
47+
* @return array
48+
* @throws LocalizedException
49+
*/
50+
public function execute(array $linkedProductIds, string $websiteId): array
51+
{
52+
$linkedStoreProductIds = [];
53+
$mainTable = $this->productLink->getMainTable();
54+
$catalogProductWebsite = $this->productLink->getTable('catalog_product_website');
55+
if (!empty($linkedProductIds)) {
56+
$select = $this->connection->select();
57+
$select->from(
58+
['main_table' => $mainTable],
59+
['linked_product_id']
60+
)->join(
61+
['product_website' => $catalogProductWebsite],
62+
'main_table.linked_product_id = product_website.product_id',
63+
[]
64+
)->where('product_website.website_id = ?', $websiteId)
65+
->where('main_table.linked_product_id IN (?)', $linkedProductIds);
66+
$linkedStoreProductIds = $this->connection->fetchAll($select);
67+
}
68+
return !empty($linkedStoreProductIds) ?
69+
array_column($linkedStoreProductIds, 'linked_product_id')
70+
:[];
71+
}
72+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GraphQl\RelatedProduct;
9+
10+
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
11+
use Magento\Store\Test\Fixture\Group as StoreGroupFixture;
12+
use Magento\Store\Test\Fixture\Store as StoreFixture;
13+
use Magento\Store\Test\Fixture\Website as WebsiteFixture;
14+
use Magento\TestFramework\Fixture\DataFixture;
15+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
16+
use Magento\TestFramework\TestCase\GraphQlAbstract;
17+
18+
/**
19+
* Test coverage for multi store related products.
20+
*/
21+
class MultiStoreRelatedProductsTest extends GraphQlAbstract
22+
{
23+
/**
24+
* Test query with multi store related products count test
25+
*
26+
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
27+
*/
28+
#[
29+
DataFixture(WebsiteFixture::class, ['name' => 'Website 1'], as: 'website1'),
30+
DataFixture(StoreGroupFixture::class, ['name' => 'StoreGroup 1',
31+
'website_id' => '$website1.id$'], as: 'store_group1'),
32+
DataFixture(StoreFixture::class, ['name' => 'Store 1',
33+
'store_group_id' => '$store_group1.id$'], as: 'store1'),
34+
DataFixture(WebsiteFixture::class, ['name' => 'Website 2'], as: 'website2'),
35+
DataFixture(StoreGroupFixture::class, ['name' => 'StoreGroup 2',
36+
'website_id' => '$website2.id$'], as: 'store_group2'),
37+
DataFixture(StoreFixture::class, ['name' => 'Store 2',
38+
'store_group_id' => '$store_group2.id$'], as: 'store2'),
39+
DataFixture(ProductFixture::class, ['name' =>'Website 1 Product A',
40+
'sku' => 'Website 1 Product A', 'price' => 20,
41+
'website_ids' => ['$website1.id$']], as: 'product2'),
42+
DataFixture(ProductFixture::class, ['name' =>'Website 1 Product B',
43+
'sku' => 'Website 1 Product B', 'price' => 30,
44+
'website_ids' => ['$website1.id$']], as: 'product3'),
45+
DataFixture(ProductFixture::class, ['name' =>'Website 2 Product Y',
46+
'sku' => 'Website 2 Product Y', 'price' => 20,
47+
'website_ids' => ['$website2.id$']], as: 'product4'),
48+
DataFixture(ProductFixture::class, ['name' =>'Website 2 Product Z',
49+
'sku' => 'Website 2 Product Z', 'price' => 30,
50+
'website_ids' => ['$website2.id$']], as: 'product5'),
51+
DataFixture(ProductFixture::class, ['name' =>'Website 1-2 Product M',
52+
'sku' => 'Website 1-2 Product M', 'price' => 30,
53+
'website_ids' => ['$website1.id$', '$website2.id$']], as: 'product6'),
54+
DataFixture(ProductFixture::class, ['name' =>'Global Product', 'sku' => 'global-product', 'price' => 10,
55+
'website_ids' => [1, '$website1.id$', '$website2.id$'],
56+
'product_links' => ['$product2.sku$','$product3.sku$',
57+
'$product4.sku$','$product5.sku$','$product6.sku$']], as: 'product1'),
58+
]
59+
public function testQueryRelatedProductsForMultiStore()
60+
{
61+
$headerMapWebsite1Store['Store'] = DataFixtureStorageManager::getStorage()->get('store1')->getCode();
62+
$headerMapWebsite2Store['Store'] = DataFixtureStorageManager::getStorage()->get('store2')->getCode();
63+
64+
$productSku = 'global-product';
65+
66+
$query = <<<QUERY
67+
{
68+
products(filter: {sku: {eq: "{$productSku}"}})
69+
{
70+
items {
71+
sku,
72+
name,
73+
related_products
74+
{
75+
sku
76+
name
77+
websites {
78+
code
79+
}
80+
}
81+
}
82+
}
83+
}
84+
QUERY;
85+
//graphQl query for store1
86+
$response = $this->graphQlQuery($query, [], '', $headerMapWebsite1Store);
87+
$this->assertRelatedProducts($response);
88+
89+
//graphQl query for store2
90+
$response = $this->graphQlQuery($query, [], '', $headerMapWebsite2Store);
91+
$this->assertRelatedProducts($response);
92+
}
93+
94+
/**
95+
* Assert related products with count
96+
*
97+
* @param float|int|bool|array|string $response
98+
* @return void
99+
*/
100+
private function assertRelatedProducts(float|int|bool|array|string $response): void
101+
{
102+
self::assertArrayHasKey('products', $response);
103+
self::assertArrayHasKey('items', $response['products']);
104+
self::assertCount(1, $response['products']['items']);
105+
self::assertArrayHasKey(0, $response['products']['items']);
106+
self::assertArrayHasKey('related_products', $response['products']['items'][0]);
107+
$relatedProducts = $response['products']['items'][0]['related_products'];
108+
self::assertCount(3, $relatedProducts);
109+
}
110+
}

0 commit comments

Comments
 (0)