Skip to content

Commit f4a11e9

Browse files
merge magento/2.3-develop into magento-performance/MC-18719
2 parents c5af561 + 50a085d commit f4a11e9

File tree

15 files changed

+870
-395
lines changed

15 files changed

+870
-395
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 <core@magentocommerce.com>
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
*
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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\CatalogUrlRewriteGraphQl\Model\Resolver;
9+
10+
use Magento\Store\Api\Data\StoreInterface;
11+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
12+
use Magento\Framework\GraphQl\Config\Element\Field;
13+
use Magento\Framework\GraphQl\Query\ResolverInterface;
14+
use Magento\Store\Model\ScopeInterface;
15+
use Magento\Framework\App\Config\ScopeConfigInterface;
16+
17+
/**
18+
* Returns the url suffix for category
19+
*/
20+
class CategoryUrlSuffix implements ResolverInterface
21+
{
22+
/**
23+
* System setting for the url suffix for categories
24+
*
25+
* @var string
26+
*/
27+
private static $xml_path_category_url_suffix = 'catalog/seo/category_url_suffix';
28+
29+
/**
30+
* Cache for product rewrite suffix
31+
*
32+
* @var array
33+
*/
34+
private $categoryUrlSuffix = [];
35+
36+
/**
37+
* @var ScopeConfigInterface
38+
*/
39+
private $scopeConfig;
40+
41+
/**
42+
* @param ScopeConfigInterface $scopeConfig
43+
*/
44+
public function __construct(ScopeConfigInterface $scopeConfig)
45+
{
46+
$this->scopeConfig = $scopeConfig;
47+
}
48+
49+
/**
50+
* @inheritdoc
51+
*/
52+
public function resolve(
53+
Field $field,
54+
$context,
55+
ResolveInfo $info,
56+
array $value = null,
57+
array $args = null
58+
): string {
59+
/** @var StoreInterface $store */
60+
$store = $context->getExtensionAttributes()->getStore();
61+
$storeId = (int)$store->getId();
62+
return $this->getCategoryUrlSuffix($storeId);
63+
}
64+
65+
/**
66+
* Retrieve category url suffix by store
67+
*
68+
* @param int $storeId
69+
* @return string
70+
*/
71+
private function getCategoryUrlSuffix(int $storeId): string
72+
{
73+
if (!isset($this->categoryUrlSuffix[$storeId])) {
74+
$this->categoryUrlSuffix[$storeId] = $this->scopeConfig->getValue(
75+
self::$xml_path_category_url_suffix,
76+
ScopeInterface::SCOPE_STORE,
77+
$storeId
78+
);
79+
}
80+
return $this->categoryUrlSuffix[$storeId];
81+
}
82+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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\CatalogUrlRewriteGraphQl\Model\Resolver;
9+
10+
use Magento\Store\Api\Data\StoreInterface;
11+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
12+
use Magento\Framework\GraphQl\Config\Element\Field;
13+
use Magento\Framework\GraphQl\Query\ResolverInterface;
14+
use Magento\Store\Model\ScopeInterface;
15+
use Magento\Framework\App\Config\ScopeConfigInterface;
16+
17+
/**
18+
* Returns the url suffix for product
19+
*/
20+
class ProductUrlSuffix implements ResolverInterface
21+
{
22+
/**
23+
* System setting for the url suffix for products
24+
*
25+
* @var string
26+
*/
27+
private static $xml_path_product_url_suffix = 'catalog/seo/product_url_suffix';
28+
29+
/**
30+
* Cache for product rewrite suffix
31+
*
32+
* @var array
33+
*/
34+
private $productUrlSuffix = [];
35+
36+
/**
37+
* @var ScopeConfigInterface
38+
*/
39+
private $scopeConfig;
40+
41+
/**
42+
* @param ScopeConfigInterface $scopeConfig
43+
*/
44+
public function __construct(ScopeConfigInterface $scopeConfig)
45+
{
46+
$this->scopeConfig = $scopeConfig;
47+
}
48+
49+
/**
50+
* @inheritdoc
51+
*/
52+
public function resolve(
53+
Field $field,
54+
$context,
55+
ResolveInfo $info,
56+
array $value = null,
57+
array $args = null
58+
): string {
59+
/** @var StoreInterface $store */
60+
$store = $context->getExtensionAttributes()->getStore();
61+
$storeId = (int)$store->getId();
62+
return $this->getProductUrlSuffix($storeId);
63+
}
64+
65+
/**
66+
* Retrieve product url suffix by store
67+
*
68+
* @param int $storeId
69+
* @return string
70+
*/
71+
private function getProductUrlSuffix(int $storeId): string
72+
{
73+
if (!isset($this->productUrlSuffix[$storeId])) {
74+
$this->productUrlSuffix[$storeId] = $this->scopeConfig->getValue(
75+
self::$xml_path_product_url_suffix,
76+
ScopeInterface::SCOPE_STORE,
77+
$storeId
78+
);
79+
}
80+
return $this->productUrlSuffix[$storeId];
81+
}
82+
}

0 commit comments

Comments
 (0)