Skip to content

Commit 5b4b568

Browse files
authored
Merge pull request #281 from cod40403/LYNX-503
LYNX-503 - Grouped product thumbnail should be shown according to the configuration
2 parents 43b5806 + 97b1279 commit 5b4b568

File tree

4 files changed

+236
-89
lines changed

4 files changed

+236
-89
lines changed
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+
* NOTICE: All information contained herein is, and remains
7+
* the property of Adobe and its suppliers, if any. The intellectual
8+
* and technical concepts contained herein are proprietary to Adobe
9+
* and its suppliers and are protected by all applicable intellectual
10+
* property laws, including trade secret and copyright laws.
11+
* Dissemination of this information or reproduction of this material
12+
* is strictly forbidden unless prior written permission is obtained
13+
* from Adobe.
14+
*/
15+
declare(strict_types=1);
16+
17+
namespace Magento\GroupedProductGraphQl\Plugin\Model\Resolver;
18+
19+
use Magento\CatalogGraphQl\Model\Resolver\Product\ProductImage as Subject;
20+
use Magento\Framework\GraphQl\Config\Element\Field;
21+
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
22+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
23+
use Magento\Quote\Model\Quote\Item;
24+
use Magento\StoreGraphQl\Model\Resolver\Store\StoreConfigDataProvider;
25+
26+
class ProductImagePlugin
27+
{
28+
private const CONF_GROUPED_PRODUCT_IMAGE = 'grouped_product_image';
29+
30+
private const CONF_GROUPED_PRODUCT_IMAGE_PARENT = 'parent';
31+
32+
private const FIELD_THUMBNAIL = 'thumbnail';
33+
34+
private const PRODUCT_TYPE_SIMPLE = 'simple';
35+
36+
private const PRODUCT_TYPE_GROUPED = 'grouped';
37+
38+
/**
39+
* ProductImagePlugin Constructor
40+
*
41+
* @param StoreConfigDataProvider $storeConfigDataProvider
42+
*/
43+
public function __construct(
44+
private readonly StoreConfigDataProvider $storeConfigDataProvider,
45+
) {
46+
}
47+
48+
/**
49+
* Update product thumbnail URL to parent product's thumbnail URL for grouped product
50+
*
51+
* @param Subject $subject
52+
* @param array $returnArray
53+
* @param Field $field
54+
* @param ContextInterface $context
55+
* @param ResolveInfo $info
56+
* @param array|null $value
57+
* @param array|null $args
58+
* @return array
59+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
60+
*/
61+
public function afterResolve(
62+
Subject $subject,
63+
array $returnArray,
64+
Field $field,
65+
ContextInterface $context,
66+
ResolveInfo $info,
67+
array $value = null,
68+
array $args = null
69+
): array {
70+
71+
/* @var $cartItem Item */
72+
$cartItem = $value['cart_item'] ?? [];
73+
if (!$cartItem instanceof Item ||
74+
!isset($cartItem['product_type']) ||
75+
$cartItem['product_type'] !== self::PRODUCT_TYPE_SIMPLE ||
76+
$field->getName() !== self::FIELD_THUMBNAIL) {
77+
return $returnArray;
78+
}
79+
80+
$storeConfigData = $this->storeConfigDataProvider->getStoreConfigData(
81+
$context->getExtensionAttributes()->getStore()
82+
);
83+
if ($storeConfigData[self::CONF_GROUPED_PRODUCT_IMAGE] !== self::CONF_GROUPED_PRODUCT_IMAGE_PARENT) {
84+
return $returnArray;
85+
}
86+
87+
return $this->updateThumbnailToParentThumbnail($cartItem, $returnArray);
88+
}
89+
90+
/**
91+
* Update thumbnail URL to parent thumbnail URL
92+
*
93+
* @param Item $cartItem
94+
* @param array $returnArray
95+
* @return array
96+
*/
97+
private function updateThumbnailToParentThumbnail(Item $cartItem, array $returnArray): array
98+
{
99+
foreach ($cartItem->getOptions() as $option) {
100+
$parentProduct = $option->getProduct();
101+
102+
if ($parentProduct->getTypeId() === self::PRODUCT_TYPE_GROUPED && $parentProduct->getThumbnail()) {
103+
$returnArray['model']['thumbnail'] = $parentProduct->getThumbnail();
104+
break;
105+
}
106+
}
107+
108+
return $returnArray;
109+
}
110+
}

app/code/Magento/GroupedProductGraphQl/composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
"magento/module-grouped-product": "*",
88
"magento/module-catalog": "*",
99
"magento/module-catalog-graph-ql": "*",
10-
"magento/framework": "*"
10+
"magento/framework": "*",
11+
"magento/module-store-graph-ql": "*",
12+
"magento/module-quote": "*"
1113
},
1214
"license": [
1315
"OSL-3.0",

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,8 @@
5353
</argument>
5454
</arguments>
5555
</type>
56+
57+
<type name="Magento\CatalogGraphQl\Model\Resolver\Product\ProductImage">
58+
<plugin name="updateThumbnailToParentProductThumbnail" type="Magento\GroupedProductGraphQl\Plugin\Model\Resolver\ProductImagePlugin" />
59+
</type>
5660
</config>

dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/AddGroupedProductToCartThumbnailTest.php

Lines changed: 119 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
namespace Magento\GraphQl\Quote;
1818

19+
use Exception;
20+
use Magento\Catalog\Api\ProductRepositoryInterface;
21+
use Magento\Framework\Exception\LocalizedException;
1922
use Magento\TestFramework\Fixture\Config as ConfigFixture;
2023
use Magento\Catalog\Test\Fixture\Category as CategoryFixture;
2124
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
@@ -24,70 +27,138 @@
2427
use Magento\Quote\Test\Fixture\QuoteIdMask as QuoteMaskFixture;
2528
use Magento\TestFramework\Fixture\DataFixture;
2629
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
30+
use Magento\TestFramework\Helper\Bootstrap;
2731
use Magento\TestFramework\TestCase\GraphQlAbstract;
2832

2933
/**
3034
* Test adding grouped products returns default thumbnail/product image thumbnail
3135
*/
36+
#[
37+
DataFixture(CategoryFixture::class, as: 'category'),
38+
DataFixture(
39+
ProductFixture::class,
40+
[
41+
'category_ids' => ['$category.id$'],
42+
'media_gallery_entries' => [
43+
[
44+
'label' => 'image',
45+
'media_type' => 'image',
46+
'position' => 1,
47+
'disabled' => false,
48+
'types' => [
49+
'image',
50+
'small_image',
51+
'thumbnail'
52+
],
53+
'file' => '/m/product1.jpg',
54+
],
55+
],
56+
],
57+
'product1'
58+
),
59+
DataFixture(
60+
ProductFixture::class,
61+
[
62+
'category_ids' => ['$category.id$'],
63+
'media_gallery_entries' => [
64+
[
65+
'label' => 'image',
66+
'media_type' => 'image',
67+
'position' => 1,
68+
'disabled' => false,
69+
'types' => [
70+
'image',
71+
'small_image',
72+
'thumbnail'
73+
],
74+
'file' => '/m/product2.jpg',
75+
],
76+
],
77+
],
78+
'product2'
79+
),
80+
DataFixture(
81+
GroupedProductFixture::class,
82+
[
83+
'category_ids' => ['$category.id$'],
84+
'product_links' => [
85+
['sku' => '$product1.sku$', 'qty' => 1],
86+
['sku' => '$product2.sku$', 'qty' => 1]
87+
]
88+
],
89+
'grouped-product'
90+
),
91+
DataFixture(GuestCartFixture::class, as: 'cart'),
92+
DataFixture(QuoteMaskFixture::class, ['cart_id' => '$cart.id$'], 'quoteIdMask'),
93+
]
3294
class AddGroupedProductToCartThumbnailTest extends GraphQlAbstract
3395
{
3496
private const DEFAULT_THUMBNAIL_PATH = 'Magento_Catalog/images/product/placeholder/thumbnail.jpg';
3597

98+
/**
99+
* @throws LocalizedException
100+
*/
36101
#[
37-
DataFixture(CategoryFixture::class, ['name' => 'Category'], 'category'),
38-
DataFixture(
39-
ProductFixture::class,
40-
[
41-
'name' => 'Product 1',
42-
'sku' => 'product-1',
43-
'category_ids' => ['$category.id$'],
44-
'price' => 10
45-
],
46-
'product1'
47-
),
48-
DataFixture(
49-
ProductFixture::class,
50-
[
51-
'name' => 'Product 2',
52-
'sku' => 'product-2',
53-
'category_ids' => ['$category.id$'],
54-
'price' => 15
55-
],
56-
'product2'
57-
),
58-
DataFixture(
59-
GroupedProductFixture::class,
60-
[
61-
'sku' => 'grouped-product',
62-
'category_ids' => ['$category.id$'],
63-
'product_links' => [
64-
['sku' => '$product1.sku$', 'qty' => 1],
65-
['sku' => '$product2.sku$', 'qty' => 1]
66-
]
67-
],
68-
'grouped-product'
69-
),
70-
DataFixture(GuestCartFixture::class, as: 'cart'),
71-
DataFixture(QuoteMaskFixture::class, ['cart_id' => '$cart.id$'], 'quoteIdMask'),
102+
ConfigFixture('checkout/cart/grouped_product_image', 'parent'),
72103
]
73-
public function testAddGroupedProductToCartWithoutImageShouldUseThumbnail()
104+
public function testAddGroupedProductToCartWithImageShouldUseParentImageAsThumbnail()
105+
{
106+
$thumbnails = [
107+
'product1' => self::DEFAULT_THUMBNAIL_PATH,
108+
'product2' => self::DEFAULT_THUMBNAIL_PATH
109+
];
110+
$this->assertProductThumbnailUrl($thumbnails);
111+
}
112+
113+
/**
114+
* @throws LocalizedException
115+
*/
116+
#[
117+
ConfigFixture('checkout/cart/grouped_product_image', 'itself'),
118+
]
119+
public function testAddGroupedProductToCartWithImageShouldUseProductImageAsThumbnail()
120+
{
121+
$productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
122+
$product1Sku = DataFixtureStorageManager::getStorage()->get('product1')->getSku();
123+
$product2Sku = DataFixtureStorageManager::getStorage()->get('product2')->getSku();
124+
$thumbnails = [
125+
'product1' => $productRepository->get($product1Sku)->getThumbnail(),
126+
'product2' => $productRepository->get($product2Sku)->getThumbnail()
127+
];
128+
129+
$this->assertProductThumbnailUrl($thumbnails);
130+
}
131+
132+
/**
133+
* Assert product thumbnail url
134+
*
135+
* @param array $thumbnails
136+
* @return void
137+
* @throws LocalizedException
138+
* @throws Exception
139+
*/
140+
private function assertProductThumbnailUrl(array $thumbnails): void
74141
{
75142
$cartId = DataFixtureStorageManager::getStorage()->get('quoteIdMask')->getMaskedId();
76-
$groupedProductId = DataFixtureStorageManager::getStorage()->get('grouped-product')->getSku();
77-
$response = $this->graphQlMutation($this->getMutation($cartId, $groupedProductId));
143+
$groupedProductSku = DataFixtureStorageManager::getStorage()->get('grouped-product')->getSku();
144+
$response = $this->graphQlMutation($this->getMutation($cartId, $groupedProductSku));
78145

79146
$this->assertArrayHasKey('addProductsToCart', $response);
80147
$this->assertEquals(2, count($response['addProductsToCart']['cart']['itemsV2']['items']));
148+
81149
$this->assertStringContainsString(
82-
self::DEFAULT_THUMBNAIL_PATH,
150+
$thumbnails['product1'],
83151
$response['addProductsToCart']['cart']['itemsV2']['items'][0]['product']['thumbnail']['url']
84152
);
85153
$this->assertStringContainsString(
86-
self::DEFAULT_THUMBNAIL_PATH,
154+
$thumbnails['product2'],
87155
$response['addProductsToCart']['cart']['itemsV2']['items'][1]['product']['thumbnail']['url']
88156
);
89157
}
90158

159+
/**
160+
* @throws LocalizedException
161+
*/
91162
#[
92163
ConfigFixture('checkout/cart/grouped_product_image', 'itself'),
93164
DataFixture(CategoryFixture::class, ['name' => 'Category'], 'category'),
@@ -97,21 +168,7 @@ public function testAddGroupedProductToCartWithoutImageShouldUseThumbnail()
97168
'name' => 'Product 1',
98169
'sku' => 'product-1',
99170
'category_ids' => ['$category.id$'],
100-
'price' => 10,
101-
'media_gallery_entries' => [
102-
[
103-
'label' => 'image',
104-
'media_type' => 'image',
105-
'position' => 1,
106-
'disabled' => false,
107-
'types' => [
108-
'image',
109-
'small_image',
110-
'thumbnail'
111-
],
112-
'file' => '/m/product1.jpg',
113-
],
114-
],
171+
'price' => 10
115172
],
116173
'product1'
117174
),
@@ -121,21 +178,7 @@ public function testAddGroupedProductToCartWithoutImageShouldUseThumbnail()
121178
'name' => 'Product 2',
122179
'sku' => 'product-2',
123180
'category_ids' => ['$category.id$'],
124-
'price' => 15,
125-
'media_gallery_entries' => [
126-
[
127-
'label' => 'image',
128-
'media_type' => 'image',
129-
'position' => 1,
130-
'disabled' => false,
131-
'types' => [
132-
'image',
133-
'small_image',
134-
'thumbnail'
135-
],
136-
'file' => '/m/product2.jpg',
137-
],
138-
],
181+
'price' => 15
139182
],
140183
'product2'
141184
),
@@ -154,25 +197,13 @@ public function testAddGroupedProductToCartWithoutImageShouldUseThumbnail()
154197
DataFixture(GuestCartFixture::class, as: 'cart'),
155198
DataFixture(QuoteMaskFixture::class, ['cart_id' => '$cart.id$'], 'quoteIdMask'),
156199
]
157-
public function testAddGroupedProductToCartWithImageShouldUseProductImageAsThumbnail()
200+
public function testAddGroupedProductToCartWithoutImageShouldUseThumbnail()
158201
{
159-
$cartId = DataFixtureStorageManager::getStorage()->get('quoteIdMask')->getMaskedId();
160-
$groupedProductId = DataFixtureStorageManager::getStorage()->get('grouped-product')->getSku();
161-
$product1 = DataFixtureStorageManager::getStorage()->get('product1');
162-
$product2 = DataFixtureStorageManager::getStorage()->get('product2');
163-
164-
$response = $this->graphQlMutation($this->getMutation($cartId, $groupedProductId));
165-
166-
$this->assertArrayHasKey('addProductsToCart', $response);
167-
$this->assertEquals(2, count($response['addProductsToCart']['cart']['itemsV2']['items']));
168-
$this->assertStringContainsString(
169-
$product1->getCustomAttribute('thumbnail')->getValue(),
170-
$response['addProductsToCart']['cart']['itemsV2']['items'][0]['product']['thumbnail']['url']
171-
);
172-
$this->assertStringContainsString(
173-
$product2->getCustomAttribute('thumbnail')->getValue(),
174-
$response['addProductsToCart']['cart']['itemsV2']['items'][1]['product']['thumbnail']['url']
175-
);
202+
$thumbnails = [
203+
'product1' => self::DEFAULT_THUMBNAIL_PATH,
204+
'product2' => self::DEFAULT_THUMBNAIL_PATH
205+
];
206+
$this->assertProductThumbnailUrl($thumbnails);
176207
}
177208

178209
/**

0 commit comments

Comments
 (0)