Skip to content

Commit fe280cf

Browse files
Merge remote-tracking branch 'remotes/github/MAGETWO-94565' into EPAM-PR-74
2 parents 2e4c728 + 08e446e commit fe280cf

File tree

3 files changed

+181
-16
lines changed

3 files changed

+181
-16
lines changed

app/code/Magento/Catalog/Model/ResourceModel/Category.php

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99
*
1010
* @author Magento Core Team <[email protected]>
1111
*/
12+
declare(strict_types=1);
13+
1214
namespace Magento\Catalog\Model\ResourceModel;
1315

1416
use Magento\Catalog\Model\Indexer\Category\Product\Processor;
1517
use Magento\Framework\App\ObjectManager;
1618
use Magento\Framework\DataObject;
1719
use Magento\Framework\EntityManager\EntityManager;
18-
use Magento\Catalog\Model\Category as CategoryEntity;
20+
use Magento\Catalog\Setup\CategorySetup;
1921

2022
/**
2123
* Resource model for category entity
@@ -92,6 +94,7 @@ class Category extends AbstractResource
9294
* @var Processor
9395
*/
9496
private $indexerProcessor;
97+
9598
/**
9699
* Category constructor.
97100
* @param \Magento\Eav\Model\Entity\Context $context
@@ -275,7 +278,7 @@ protected function _beforeSave(\Magento\Framework\DataObject $object)
275278
if ($object->getPosition() === null) {
276279
$object->setPosition($this->_getMaxPosition($object->getPath()) + 1);
277280
}
278-
$path = explode('/', $object->getPath());
281+
$path = explode('/', (string)$object->getPath());
279282
$level = count($path) - ($object->getId() ? 1 : 0);
280283
$toUpdateChild = array_diff($path, [$object->getId()]);
281284

@@ -314,7 +317,7 @@ protected function _afterSave(\Magento\Framework\DataObject $object)
314317
/**
315318
* Add identifier for new category
316319
*/
317-
if (substr($object->getPath(), -1) == '/') {
320+
if (substr((string)$object->getPath(), -1) == '/') {
318321
$object->setPath($object->getPath() . $object->getId());
319322
$this->_savePath($object);
320323
}
@@ -352,7 +355,7 @@ protected function _getMaxPosition($path)
352355
{
353356
$connection = $this->getConnection();
354357
$positionField = $connection->quoteIdentifier('position');
355-
$level = count(explode('/', $path));
358+
$level = count(explode('/', (string)$path));
356359
$bind = ['c_level' => $level, 'c_path' => $path . '/%'];
357360
$select = $connection->select()->from(
358361
$this->getTable('catalog_category_entity'),
@@ -717,7 +720,7 @@ public function getCategories($parent, $recursionLevel = 0, $sorted = false, $as
717720
*/
718721
public function getParentCategories($category)
719722
{
720-
$pathIds = array_reverse(explode(',', $category->getPathInStore()));
723+
$pathIds = array_reverse(explode(',', (string)$category->getPathInStore()));
721724
/** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $categories */
722725
$categories = $this->_categoryCollectionFactory->create();
723726
return $categories->setStore(
@@ -1134,4 +1137,44 @@ private function getAggregateCount()
11341137
}
11351138
return $this->aggregateCount;
11361139
}
1140+
1141+
/**
1142+
* Get category with children.
1143+
*
1144+
* @param int $categoryId
1145+
* @return array
1146+
*/
1147+
public function getCategoryWithChildren(int $categoryId): array
1148+
{
1149+
$connection = $this->getConnection();
1150+
1151+
$selectAttributeCode = $connection->select()
1152+
->from(
1153+
['eav_attribute' => $this->getTable('eav_attribute')],
1154+
['attribute_id']
1155+
)->where('entity_type_id = ?', CategorySetup::CATEGORY_ENTITY_TYPE_ID)
1156+
->where('attribute_code = ?', 'is_anchor')
1157+
->limit(1);
1158+
$isAnchorAttributeCode = $connection->fetchOne($selectAttributeCode);
1159+
if (empty($isAnchorAttributeCode) || (int)$isAnchorAttributeCode <= 0) {
1160+
return [];
1161+
}
1162+
1163+
$select = $connection->select()
1164+
->from(
1165+
['cce' => $this->getTable('catalog_category_entity')],
1166+
['entity_id', 'parent_id', 'path']
1167+
)->join(
1168+
['cce_int' => $this->getTable('catalog_category_entity_int')],
1169+
'cce.entity_id = cce_int.entity_id',
1170+
['is_anchor' => 'cce_int.value']
1171+
)->where(
1172+
'cce_int.attribute_id = ?',
1173+
$isAnchorAttributeCode
1174+
)->where(
1175+
"cce.path LIKE '%/{$categoryId}' OR cce.path LIKE '%/{$categoryId}/%'"
1176+
)->order('path');
1177+
1178+
return $connection->fetchAll($select);
1179+
}
11371180
}

app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6+
declare(strict_types=1);
67

78
namespace Magento\Catalog\Model\ResourceModel\Product;
89

@@ -22,6 +23,7 @@
2223
use Magento\Framework\Indexer\DimensionFactory;
2324
use Magento\Store\Model\Indexer\WebsiteDimensionProvider;
2425
use Magento\Store\Model\Store;
26+
use Magento\Catalog\Model\ResourceModel\Category;
2527

2628
/**
2729
* Product collection
@@ -302,6 +304,11 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac
302304
*/
303305
private $urlFinder;
304306

307+
/**
308+
* @var Category
309+
*/
310+
private $categoryResourceModel;
311+
305312
/**
306313
* Collection constructor
307314
*
@@ -330,6 +337,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac
330337
* @param TableMaintainer|null $tableMaintainer
331338
* @param PriceTableResolver|null $priceTableResolver
332339
* @param DimensionFactory|null $dimensionFactory
340+
* @param Category|null $categoryResourceModel
333341
*
334342
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
335343
*/
@@ -358,7 +366,8 @@ public function __construct(
358366
MetadataPool $metadataPool = null,
359367
TableMaintainer $tableMaintainer = null,
360368
PriceTableResolver $priceTableResolver = null,
361-
DimensionFactory $dimensionFactory = null
369+
DimensionFactory $dimensionFactory = null,
370+
Category $categoryResourceModel = null
362371
) {
363372
$this->moduleManager = $moduleManager;
364373
$this->_catalogProductFlatState = $catalogProductFlatState;
@@ -392,6 +401,8 @@ public function __construct(
392401
$this->priceTableResolver = $priceTableResolver ?: ObjectManager::getInstance()->get(PriceTableResolver::class);
393402
$this->dimensionFactory = $dimensionFactory
394403
?: ObjectManager::getInstance()->get(DimensionFactory::class);
404+
$this->categoryResourceModel = $categoryResourceModel ?: ObjectManager::getInstance()
405+
->get(Category::class);
395406
}
396407

397408
/**
@@ -1673,7 +1684,11 @@ public function addFilterByRequiredOptions()
16731684
public function setVisibility($visibility)
16741685
{
16751686
$this->_productLimitationFilters['visibility'] = $visibility;
1676-
$this->_applyProductLimitations();
1687+
if ($this->getStoreId() == Store::DEFAULT_STORE_ID) {
1688+
$this->addAttributeToFilter('visibility', $visibility);
1689+
} else {
1690+
$this->_applyProductLimitations();
1691+
}
16771692

16781693
return $this;
16791694
}
@@ -2053,12 +2068,13 @@ protected function _applyProductLimitations()
20532068
protected function _applyZeroStoreProductLimitations()
20542069
{
20552070
$filters = $this->_productLimitationFilters;
2071+
$categories = $this->getChildrenCategories((int)$filters['category_id']);
20562072

20572073
$conditions = [
20582074
'cat_pro.product_id=e.entity_id',
20592075
$this->getConnection()->quoteInto(
2060-
'cat_pro.category_id=?',
2061-
$filters['category_id']
2076+
'cat_pro.category_id IN (?)',
2077+
$categories
20622078
),
20632079
];
20642080
$joinCond = join(' AND ', $conditions);
@@ -2079,6 +2095,39 @@ protected function _applyZeroStoreProductLimitations()
20792095
return $this;
20802096
}
20812097

2098+
/**
2099+
* Get children categories.
2100+
*
2101+
* @param int $categoryId
2102+
* @return array
2103+
*/
2104+
private function getChildrenCategories(int $categoryId): array
2105+
{
2106+
$categoryIds[] = $categoryId;
2107+
$anchorCategory = [];
2108+
2109+
$categories = $this->categoryResourceModel->getCategoryWithChildren($categoryId);
2110+
if (empty($categories)) {
2111+
return $categoryIds;
2112+
}
2113+
2114+
$firstCategory = array_shift($categories);
2115+
if ($firstCategory['is_anchor'] == 1) {
2116+
$anchorCategory[] = (int)$firstCategory['entity_id'];
2117+
foreach ($categories as $category) {
2118+
if (in_array($category['parent_id'], $categoryIds)
2119+
&& in_array($category['parent_id'], $anchorCategory)) {
2120+
$categoryIds[] = (int)$category['entity_id'];
2121+
if ($category['is_anchor'] == 1) {
2122+
$anchorCategory[] = (int)$category['entity_id'];
2123+
}
2124+
}
2125+
}
2126+
}
2127+
2128+
return $categoryIds;
2129+
}
2130+
20822131
/**
20832132
* Add category ids to loaded items
20842133
*

dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,20 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6+
declare(strict_types=1);
7+
68
namespace Magento\Catalog\Model\ResourceModel\Product;
79

10+
use Magento\Catalog\Model\Product\Visibility;
11+
use Magento\Framework\App\Area;
12+
use Magento\Framework\App\State;
13+
use Magento\Store\Model\Store;
14+
use Magento\TestFramework\Helper\Bootstrap;
15+
816
/**
917
* Collection test
18+
*
19+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1020
*/
1121
class CollectionTest extends \PHPUnit\Framework\TestCase
1222
{
@@ -31,15 +41,15 @@ class CollectionTest extends \PHPUnit\Framework\TestCase
3141
*/
3242
protected function setUp()
3343
{
34-
$this->collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
44+
$this->collection = Bootstrap::getObjectManager()->create(
3545
\Magento\Catalog\Model\ResourceModel\Product\Collection::class
3646
);
3747

38-
$this->processor = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
48+
$this->processor = Bootstrap::getObjectManager()->create(
3949
\Magento\Catalog\Model\Indexer\Product\Price\Processor::class
4050
);
4151

42-
$this->productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
52+
$this->productRepository = Bootstrap::getObjectManager()->create(
4353
\Magento\Catalog\Api\ProductRepositoryInterface::class
4454
);
4555
}
@@ -54,7 +64,7 @@ public function testAddPriceDataOnSchedule()
5464
$this->processor->getIndexer()->setScheduled(true);
5565
$this->assertTrue($this->processor->getIndexer()->isScheduled());
5666

57-
$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
67+
$productRepository = Bootstrap::getObjectManager()
5868
->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
5969
/** @var \Magento\Catalog\Api\Data\ProductInterface $product */
6070
$product = $productRepository->get('simple');
@@ -73,7 +83,7 @@ public function testAddPriceDataOnSchedule()
7383
//reindexing
7484
$this->processor->getIndexer()->reindexList([1]);
7585

76-
$this->collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
86+
$this->collection = Bootstrap::getObjectManager()->create(
7787
\Magento\Catalog\Model\ResourceModel\Product\Collection::class
7888
);
7989
$this->collection->addPriceData(0, 1);
@@ -89,6 +99,69 @@ public function testAddPriceDataOnSchedule()
8999
$this->processor->getIndexer()->setScheduled(false);
90100
}
91101

102+
/**
103+
* @magentoDataFixture Magento/Catalog/_files/products.php
104+
* @magentoAppIsolation enabled
105+
* @magentoDbIsolation disabled
106+
*/
107+
public function testSetVisibility()
108+
{
109+
$appState = Bootstrap::getObjectManager()
110+
->create(State::class);
111+
$appState->setAreaCode(Area::AREA_CRONTAB);
112+
$this->collection->setStoreId(Store::DEFAULT_STORE_ID);
113+
$this->collection->setVisibility([Visibility::VISIBILITY_BOTH]);
114+
$this->collection->load();
115+
/** @var \Magento\Catalog\Api\Data\ProductInterface[] $product */
116+
$items = $this->collection->getItems();
117+
$this->assertCount(2, $items);
118+
}
119+
120+
/**
121+
* @magentoDataFixture Magento/Catalog/_files/category_product.php
122+
* @magentoAppIsolation enabled
123+
* @magentoDbIsolation disabled
124+
*/
125+
public function testSetCategoryWithStoreFilter()
126+
{
127+
$appState = Bootstrap::getObjectManager()
128+
->create(State::class);
129+
$appState->setAreaCode(Area::AREA_CRONTAB);
130+
131+
$category = \Magento\Framework\App\ObjectManager::getInstance()->get(
132+
\Magento\Catalog\Model\Category::class
133+
)->load(333);
134+
$this->collection->addCategoryFilter($category)->addStoreFilter(1);
135+
$this->collection->load();
136+
137+
$collectionStoreFilterAfter = Bootstrap::getObjectManager()->create(
138+
\Magento\Catalog\Model\ResourceModel\Product\CollectionFactory::class
139+
)->create();
140+
$collectionStoreFilterAfter->addStoreFilter(1)->addCategoryFilter($category);
141+
$collectionStoreFilterAfter->load();
142+
$this->assertEquals($this->collection->getItems(), $collectionStoreFilterAfter->getItems());
143+
$this->assertCount(1, $collectionStoreFilterAfter->getItems());
144+
}
145+
146+
/**
147+
* @magentoDataFixture Magento/Catalog/_files/categories.php
148+
* @magentoAppIsolation enabled
149+
* @magentoDbIsolation disabled
150+
*/
151+
public function testSetCategoryFilter()
152+
{
153+
$appState = Bootstrap::getObjectManager()
154+
->create(State::class);
155+
$appState->setAreaCode(Area::AREA_CRONTAB);
156+
157+
$category = \Magento\Framework\App\ObjectManager::getInstance()->get(
158+
\Magento\Catalog\Model\Category::class
159+
)->load(3);
160+
$this->collection->addCategoryFilter($category);
161+
$this->collection->load();
162+
$this->assertEquals($this->collection->getSize(), 3);
163+
}
164+
92165
/**
93166
* @magentoDataFixture Magento/Catalog/_files/products.php
94167
* @magentoAppIsolation enabled
@@ -98,7 +171,7 @@ public function testAddPriceDataOnSave()
98171
{
99172
$this->processor->getIndexer()->setScheduled(false);
100173
$this->assertFalse($this->processor->getIndexer()->isScheduled());
101-
$productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
174+
$productRepository = Bootstrap::getObjectManager()
102175
->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
103176
/** @var \Magento\Catalog\Api\Data\ProductInterface $product */
104177
$product = $productRepository->get('simple');
@@ -184,7 +257,7 @@ public function testJoinTable()
184257
$productTable = $this->collection->getTable('catalog_product_entity');
185258
$urlRewriteTable = $this->collection->getTable('url_rewrite');
186259

187-
// phpcs:ignore
260+
// phpcs:ignore Magento2.SQL.RawQuery
188261
$expected = 'SELECT `e`.*, `alias`.`request_path` FROM `' . $productTable . '` AS `e`'
189262
. ' LEFT JOIN `' . $urlRewriteTable . '` AS `alias` ON (alias.entity_id =e.entity_id)'
190263
. ' AND (alias.entity_type = \'product\')';

0 commit comments

Comments
 (0)