Skip to content

Commit 806602c

Browse files
committed
ACP2E-2885: Products graphql returned disabled categories in the category aggregations
1 parent c26aca7 commit 806602c

File tree

5 files changed

+155
-45
lines changed

5 files changed

+155
-45
lines changed

app/code/Magento/Catalog/Model/Indexer/Category/Product/AbstractAction.php

Lines changed: 54 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
namespace Magento\Catalog\Model\Indexer\Category\Product;
88

9+
use Magento\Catalog\Api\Data\CategoryInterface;
910
use Magento\Catalog\Api\Data\ProductInterface;
11+
use Magento\Catalog\Model\Category;
1012
use Magento\Catalog\Model\Product;
1113
use Magento\Framework\App\ObjectManager;
1214
use Magento\Framework\App\ResourceConnection;
@@ -303,19 +305,17 @@ protected function getPathFromCategoryId($categoryId)
303305
protected function getNonAnchorCategoriesSelect(Store $store)
304306
{
305307
if (!isset($this->nonAnchorSelects[$store->getId()])) {
306-
$statusAttributeId = $this->config->getAttribute(
307-
Product::ENTITY,
308-
'status'
309-
)->getId();
310-
$visibilityAttributeId = $this->config->getAttribute(
311-
Product::ENTITY,
312-
'visibility'
313-
)->getId();
308+
$statusAttributeId = $this->config->getAttribute(Product::ENTITY, 'status')->getId();
309+
$visibilityAttributeId = $this->config->getAttribute(Product::ENTITY, 'visibility')->getId();
310+
$isActiveAttributeId = $this->config->getAttribute(Category::ENTITY, 'is_active')->getId();
314311

315312
$rootPath = $this->getPathFromCategoryId($store->getRootCategoryId());
316313

317-
$metadata = $this->metadataPool->getMetadata(ProductInterface::class);
318-
$linkField = $metadata->getLinkField();
314+
$productMetadata = $this->metadataPool->getMetadata(ProductInterface::class);
315+
$productLinkField = $productMetadata->getLinkField();
316+
$categoryMetadata = $this->metadataPool->getMetadata(CategoryInterface::class);
317+
$categoryLinkField = $categoryMetadata->getLinkField();
318+
319319
$select = $this->connection->select()->from(
320320
['cc' => $this->getTable('catalog_category_entity')],
321321
[]
@@ -333,28 +333,37 @@ protected function getNonAnchorCategoriesSelect(Store $store)
333333
[]
334334
)->joinInner(
335335
['cpsd' => $this->getTable('catalog_product_entity_int')],
336-
'cpsd.' . $linkField . ' = cpe.' . $linkField . ' AND cpsd.store_id = 0' .
337-
' AND cpsd.attribute_id = ' .
338-
$statusAttributeId,
336+
'cpsd.' . $productLinkField . ' = cpe.' . $productLinkField . ' AND cpsd.store_id = 0' .
337+
' AND cpsd.attribute_id = ' . $statusAttributeId,
339338
[]
340339
)->joinLeft(
341340
['cpss' => $this->getTable('catalog_product_entity_int')],
342-
'cpss.' . $linkField . ' = cpe.' . $linkField . ' AND cpss.attribute_id = cpsd.attribute_id' .
343-
' AND cpss.store_id = ' .
344-
$store->getId(),
341+
'cpss.' . $productLinkField . ' = cpe.' . $productLinkField .
342+
' AND cpss.attribute_id = cpsd.attribute_id AND cpss.store_id = ' . $store->getId(),
345343
[]
346344
)->joinInner(
347345
['cpvd' => $this->getTable('catalog_product_entity_int')],
348-
'cpvd.' . $linkField . ' = cpe.' . $linkField . ' AND cpvd.store_id = 0' .
349-
' AND cpvd.attribute_id = ' .
350-
$visibilityAttributeId,
346+
'cpvd.' . $productLinkField . ' = cpe.' . $productLinkField . ' AND cpvd.store_id = 0' .
347+
' AND cpvd.attribute_id = ' . $visibilityAttributeId,
351348
[]
352349
)->joinLeft(
353350
['cpvs' => $this->getTable('catalog_product_entity_int')],
354-
'cpvs.' . $linkField . ' = cpe.' . $linkField . ' AND cpvs.attribute_id = cpvd.attribute_id' .
355-
' AND cpvs.store_id = ' .
356-
$store->getId(),
351+
'cpvs.' . $productLinkField . ' = cpe.' . $productLinkField .
352+
' AND cpvs.attribute_id = cpvd.attribute_id AND cpvs.store_id = ' . $store->getId(),
353+
[]
354+
)->joinInner(
355+
['ccacd' => $this->getTable('catalog_category_entity_int')],
356+
'ccacd.' . $categoryLinkField . ' = cc.' . $categoryLinkField . ' AND ccacd.store_id = 0' .
357+
' AND ccacd.attribute_id = ' . $isActiveAttributeId,
357358
[]
359+
)->joinLeft(
360+
['ccacs' => $this->getTable('catalog_category_entity_int')],
361+
'ccacs.' . $categoryLinkField . ' = cc.' . $categoryLinkField .
362+
' AND ccacs.attribute_id = ccacd.attribute_id AND ccacs.store_id = ' . $store->getId(),
363+
[]
364+
)->where(
365+
$this->connection->getIfNullSql('ccacs.value', 'ccacd.value') . ' = ?',
366+
1
358367
)->where(
359368
'cc.path LIKE ' . $this->connection->quote($rootPath . '/%')
360369
)->where(
@@ -525,10 +534,8 @@ protected function hasAnchorSelect(Store $store)
525534
protected function createAnchorSelect(Store $store)
526535
{
527536
$this->setCurrentStore($store);
528-
$isAnchorAttributeId = $this->config->getAttribute(
529-
\Magento\Catalog\Model\Category::ENTITY,
530-
'is_anchor'
531-
)->getId();
537+
$isAnchorAttributeId = $this->config->getAttribute(Category::ENTITY, 'is_anchor')->getId();
538+
$isActiveAttributeId = $this->config->getAttribute(Category::ENTITY, 'is_active')->getId();
532539
$statusAttributeId = $this->config->getAttribute(Product::ENTITY, 'status')->getId();
533540
$visibilityAttributeId = $this->config->getAttribute(Product::ENTITY, 'visibility')->getId();
534541
$rootCatIds = explode('/', $this->getPathFromCategoryId($store->getRootCategoryId()));
@@ -537,7 +544,7 @@ protected function createAnchorSelect(Store $store)
537544
$temporaryTreeTable = $this->makeTempCategoryTreeIndex();
538545

539546
$productMetadata = $this->metadataPool->getMetadata(ProductInterface::class);
540-
$categoryMetadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\CategoryInterface::class);
547+
$categoryMetadata = $this->metadataPool->getMetadata(CategoryInterface::class);
541548
$productLinkField = $productMetadata->getLinkField();
542549
$categoryLinkField = $categoryMetadata->getLinkField();
543550

@@ -575,21 +582,18 @@ protected function createAnchorSelect(Store $store)
575582
[]
576583
)->joinLeft(
577584
['cpss' => $this->getTable('catalog_product_entity_int')],
578-
'cpss.' . $productLinkField . ' = cpe.' . $productLinkField . ' AND cpss.attribute_id = cpsd.attribute_id' .
579-
' AND cpss.store_id = ' .
580-
$store->getId(),
585+
'cpss.' . $productLinkField . ' = cpe.' . $productLinkField .
586+
' AND cpss.attribute_id = cpsd.attribute_id AND cpss.store_id = ' . $store->getId(),
581587
[]
582588
)->joinInner(
583589
['cpvd' => $this->getTable('catalog_product_entity_int')],
584590
'cpvd.' . $productLinkField . ' = cpe. ' . $productLinkField . ' AND cpvd.store_id = 0' .
585-
' AND cpvd.attribute_id = ' .
586-
$visibilityAttributeId,
591+
' AND cpvd.attribute_id = ' . $visibilityAttributeId,
587592
[]
588593
)->joinLeft(
589594
['cpvs' => $this->getTable('catalog_product_entity_int')],
590595
'cpvs.' . $productLinkField . ' = cpe.' . $productLinkField .
591-
' AND cpvs.attribute_id = cpvd.attribute_id ' . 'AND cpvs.store_id = ' .
592-
$store->getId(),
596+
' AND cpvs.attribute_id = cpvd.attribute_id ' . 'AND cpvs.store_id = ' . $store->getId(),
593597
[]
594598
)->joinInner(
595599
['ccad' => $this->getTable('catalog_category_entity_int')],
@@ -599,9 +603,21 @@ protected function createAnchorSelect(Store $store)
599603
)->joinLeft(
600604
['ccas' => $this->getTable('catalog_category_entity_int')],
601605
'ccas.' . $categoryLinkField . ' = cc.' . $categoryLinkField
602-
. ' AND ccas.attribute_id = ccad.attribute_id AND ccas.store_id = ' .
603-
$store->getId(),
606+
. ' AND ccas.attribute_id = ccad.attribute_id AND ccas.store_id = ' . $store->getId(),
604607
[]
608+
)->joinInner(
609+
['ccacd' => $this->getTable('catalog_category_entity_int')],
610+
'ccacd.' . $categoryLinkField . ' = cc.' . $categoryLinkField . ' AND ccacd.store_id = 0' .
611+
' AND ccacd.attribute_id = ' . $isActiveAttributeId,
612+
[]
613+
)->joinLeft(
614+
['ccacs' => $this->getTable('catalog_category_entity_int')],
615+
'ccacs.' . $categoryLinkField . ' = cc.' . $categoryLinkField
616+
. ' AND ccacs.attribute_id = ccacd.attribute_id AND ccacs.store_id = ' . $store->getId(),
617+
[]
618+
)->where(
619+
$this->connection->getIfNullSql('ccacs.value', 'ccacd.value') . ' = ?',
620+
1
605621
)->where(
606622
'cpw.website_id = ?',
607623
$store->getWebsiteId()
@@ -711,11 +727,8 @@ protected function makeTempCategoryTreeIndex()
711727
*/
712728
protected function fillTempCategoryTreeIndex($temporaryName)
713729
{
714-
$isActiveAttributeId = $this->config->getAttribute(
715-
\Magento\Catalog\Model\Category::ENTITY,
716-
'is_active'
717-
)->getId();
718-
$categoryMetadata = $this->metadataPool->getMetadata(\Magento\Catalog\Api\Data\CategoryInterface::class);
730+
$isActiveAttributeId = $this->config->getAttribute(Category::ENTITY, 'is_active')->getId();
731+
$categoryMetadata = $this->metadataPool->getMetadata(CategoryInterface::class);
719732
$categoryLinkField = $categoryMetadata->getLinkField();
720733
$selects = $this->prepareSelectsByRange(
721734
$this->connection->select()
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
/************************************************************************
3+
*
4+
* Copyright 2023 Adobe
5+
* All Rights Reserved.
6+
*
7+
* NOTICE: All information contained herein is, and remains
8+
* the property of Adobe and its suppliers, if any. The intellectual
9+
* and technical concepts contained herein are proprietary to Adobe
10+
* and its suppliers and are protected by all applicable intellectual
11+
* property laws, including trade secret and copyright laws.
12+
* Dissemination of this information or reproduction of this material
13+
* is strictly forbidden unless prior written permission is obtained
14+
* from Adobe.
15+
* ***********************************************************************
16+
*/
17+
declare(strict_types=1);
18+
19+
namespace Magento\Catalog\Test\Fixture;
20+
21+
use Magento\Catalog\Api\CategoryLinkManagementInterface;
22+
use Magento\Framework\Exception\InvalidArgumentException;
23+
use Magento\Framework\DataObject;
24+
use Magento\TestFramework\Fixture\DataFixtureInterface;
25+
26+
/**
27+
* Assigning product to categories
28+
*/
29+
class AssignCategories implements DataFixtureInterface
30+
{
31+
private const PRODUCT = 'product';
32+
private const CATEGORIES = 'categories';
33+
34+
/**
35+
* @param CategoryLinkManagementInterface $categoryLinkManagement
36+
*/
37+
public function __construct(
38+
private readonly CategoryLinkManagementInterface $categoryLinkManagement
39+
) {
40+
}
41+
42+
/**
43+
* @inheritdoc
44+
* @throws InvalidArgumentException
45+
*/
46+
public function apply(array $data = []): ?DataObject
47+
{
48+
if (empty($data[self::PRODUCT])) {
49+
throw new InvalidArgumentException(__('"%field" is required', ['field' => self::PRODUCT]));
50+
}
51+
if (empty($data[self::CATEGORIES])) {
52+
throw new InvalidArgumentException(__('"%field" is required', ['field' => self::CATEGORIES]));
53+
}
54+
if (!is_array($data[self::CATEGORIES])) {
55+
throw new InvalidArgumentException(__('"%field" must be an array', ['field' => self::CATEGORIES]));
56+
}
57+
58+
$this->categoryLinkManagement->assignProductToCategories(
59+
$data[self::PRODUCT]->getSku(),
60+
array_map(fn ($category) => $category->getId(), $data[self::CATEGORIES])
61+
);
62+
63+
return null;
64+
}
65+
}

app/code/Magento/Elasticsearch/Model/Indexer/Fulltext/Plugin/Category/Product/Action/Rows.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@ public function afterExecute(
7575
array $entityIds,
7676
bool $useTempTable = false
7777
): ActionRows {
78-
$indexer = $this->indexerRegistry->get(FulltextIndexer::INDEXER_ID);
79-
if (!empty($entityIds) && $indexer->isScheduled()) {
78+
if (!empty($entityIds)) {
8079
$productIds = [];
8180

8281
foreach ($this->storeManager->getStores() as $store) {
@@ -86,7 +85,12 @@ public function afterExecute(
8685

8786
$productIds = array_merge([], ...$productIds);
8887
if (!empty($productIds)) {
89-
$indexer->getView()->getChangelog()->addList($productIds);
88+
$indexer = $this->indexerRegistry->get(FulltextIndexer::INDEXER_ID);
89+
if ($indexer->isScheduled()) {
90+
$indexer->getView()->getChangelog()->addList($productIds);
91+
} else {
92+
$indexer->reindexList($productIds);
93+
}
9094
}
9195
}
9296

app/code/Magento/Elasticsearch/etc/indexer.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Indexer/etc/indexer.xsd">
99
<indexer id="catalogsearch_fulltext">
1010
<dependencies>
11-
<indexer id="catalog_category_product" />
11+
<indexer id="catalog_product_category" />
1212
<indexer id="cataloginventory_stock" />
1313
<indexer id="catalog_product_price" />
1414
</dependencies>

dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductSearchCategoryAggregationsTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@
77

88
namespace Magento\GraphQl\Catalog;
99

10+
use Magento\Catalog\Test\Fixture\AssignCategories as AssignCategoriesFixture;
11+
use Magento\Catalog\Test\Fixture\Category as CategoryFixture;
12+
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
1013
use Magento\Framework\GraphQl\Query\Uid;
14+
use Magento\TestFramework\Fixture\DataFixture;
15+
use Magento\TestFramework\Fixture\DataFixtureStorage;
16+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
1117
use Magento\TestFramework\Helper\Bootstrap;
1218
use Magento\TestFramework\ObjectManager;
1319
use Magento\TestFramework\TestCase\GraphQlAbstract;
@@ -17,6 +23,11 @@
1723
*/
1824
class ProductSearchCategoryAggregationsTest extends GraphQlAbstract
1925
{
26+
/**
27+
* @var DataFixtureStorage
28+
*/
29+
private $fixtures;
30+
2031
/** @var ObjectManager */
2132
private $objectManager;
2233

@@ -28,6 +39,7 @@ class ProductSearchCategoryAggregationsTest extends GraphQlAbstract
2839
*/
2940
protected function setUp(): void
3041
{
42+
$this->fixtures = DataFixtureStorageManager::getStorage();
3143
$this->objectManager = Bootstrap::getObjectManager();
3244
$this->uid = $this->objectManager->get(Uid::class);
3345
}
@@ -92,6 +104,22 @@ public function testAggregationInCategory()
92104
$this->assertEquals($expectedSubcategorie, $categoryAggregation);
93105
}
94106

107+
#[
108+
DataFixture(ProductFixture::class, as: 'prod1'),
109+
DataFixture(CategoryFixture::class, ['name' => 'Category 1'], as: 'cat1'),
110+
DataFixture(CategoryFixture::class, ['name' => 'Category 2', 'is_active' => false], as: 'cat2'),
111+
DataFixture(AssignCategoriesFixture::class, ['categories' => ['$cat1$', '$cat2$'], 'product' => '$prod1$']),
112+
]
113+
public function testAggregationDisabledCategory(): void
114+
{
115+
$cat1 = $this->fixtures->get('cat1');
116+
$cat2 = $this->fixtures->get('cat2');
117+
$filterValue = "{category_id: {in: [\"{$cat1->getId()}\",\"{$cat2->getId()}\"]}}";
118+
$categoryAggregation = $this->aggregationCategoryTesting($filterValue, 'false');
119+
$expectedCategories = [$cat1->getId() => $cat1->getName()];
120+
$this->assertEquals($expectedCategories, $categoryAggregation);
121+
}
122+
95123
/**
96124
* @param string $filterValue
97125
*

0 commit comments

Comments
 (0)