Skip to content

Commit

Permalink
Merge pull request #6263 from magento-tsg/2.4-develop-pr98
Browse files Browse the repository at this point in the history
[Arrows] Fixes for 2.4 (pr98) (2.4-develop)
  • Loading branch information
zakdma authored Oct 21, 2020
2 parents 9772213 + 9cd1d5b commit 72f9180
Show file tree
Hide file tree
Showing 9 changed files with 383 additions and 42 deletions.
57 changes: 36 additions & 21 deletions app/code/Magento/Catalog/Model/CategoryRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@

namespace Magento\Catalog\Model;

use Magento\Catalog\Model\CategoryRepository\PopulateWithValues;
use Magento\Catalog\Model\ResourceModel\Category as CategoryResource;
use Magento\Framework\Api\ExtensibleDataObjectConverter;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Exception\StateException;
use Magento\Catalog\Api\Data\CategoryInterface;
use Magento\Store\Model\StoreManagerInterface;

/**
* Repository for categories.
Expand All @@ -25,27 +31,27 @@ class CategoryRepository implements \Magento\Catalog\Api\CategoryRepositoryInter
protected $instances = [];

/**
* @var \Magento\Store\Model\StoreManagerInterface
* @var StoreManagerInterface
*/
protected $storeManager;

/**
* @var \Magento\Catalog\Model\CategoryFactory
* @var CategoryFactory
*/
protected $categoryFactory;

/**
* @var \Magento\Catalog\Model\ResourceModel\Category
* @var CategoryResource
*/
protected $categoryResource;

/**
* @var \Magento\Framework\EntityManager\MetadataPool
* @var MetadataPool
*/
protected $metadataPool;

/**
* @var \Magento\Framework\Api\ExtensibleDataObjectConverter
* @var ExtensibleDataObjectConverter
*/
private $extensibleDataObjectConverter;

Expand All @@ -57,28 +63,37 @@ class CategoryRepository implements \Magento\Catalog\Api\CategoryRepositoryInter
protected $useConfigFields = ['available_sort_by', 'default_sort_by', 'filter_price_range'];

/**
* @param \Magento\Catalog\Model\CategoryFactory $categoryFactory
* @param \Magento\Catalog\Model\ResourceModel\Category $categoryResource
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
* @var PopulateWithValues
*/
private $populateWithValues;

/**
* @param CategoryFactory $categoryFactory
* @param CategoryResource $categoryResource
* @param StoreManagerInterface $storeManager
* @param PopulateWithValues|null $populateWithValues
*/
public function __construct(
\Magento\Catalog\Model\CategoryFactory $categoryFactory,
\Magento\Catalog\Model\ResourceModel\Category $categoryResource,
\Magento\Store\Model\StoreManagerInterface $storeManager
CategoryFactory $categoryFactory,
CategoryResource $categoryResource,
StoreManagerInterface $storeManager,
?PopulateWithValues $populateWithValues
) {
$this->categoryFactory = $categoryFactory;
$this->categoryResource = $categoryResource;
$this->storeManager = $storeManager;
$objectManager = ObjectManager::getInstance();
$this->populateWithValues = $populateWithValues ?? $objectManager->get(PopulateWithValues::class);
}

/**
* @inheritdoc
*/
public function save(\Magento\Catalog\Api\Data\CategoryInterface $category)
public function save(CategoryInterface $category)
{
$storeId = (int)$this->storeManager->getStore()->getId();
$existingData = $this->getExtensibleDataObjectConverter()
->toNestedArray($category, [], \Magento\Catalog\Api\Data\CategoryInterface::class);
->toNestedArray($category, [], CategoryInterface::class);
$existingData = array_diff_key($existingData, array_flip(['path', 'level', 'parent_id']));
$existingData['store_id'] = $storeId;

Expand Down Expand Up @@ -110,7 +125,7 @@ public function save(\Magento\Catalog\Api\Data\CategoryInterface $category)
$existingData['parent_id'] = $parentId;
$existingData['level'] = null;
}
$category->addData($existingData);
$this->populateWithValues->execute($category, $existingData);
try {
$this->validateCategory($category);
$this->categoryResource->save($category);
Expand Down Expand Up @@ -151,7 +166,7 @@ public function get($categoryId, $storeId = null)
/**
* @inheritdoc
*/
public function delete(\Magento\Catalog\Api\Data\CategoryInterface $category)
public function delete(CategoryInterface $category)
{
try {
$categoryId = $category->getId();
Expand Down Expand Up @@ -213,29 +228,29 @@ protected function validateCategory(Category $category)
/**
* Lazy loader for the converter.
*
* @return \Magento\Framework\Api\ExtensibleDataObjectConverter
* @return ExtensibleDataObjectConverter
*
* @deprecated 101.0.0
*/
private function getExtensibleDataObjectConverter()
{
if ($this->extensibleDataObjectConverter === null) {
$this->extensibleDataObjectConverter = \Magento\Framework\App\ObjectManager::getInstance()
->get(\Magento\Framework\Api\ExtensibleDataObjectConverter::class);
$this->extensibleDataObjectConverter = ObjectManager::getInstance()
->get(ExtensibleDataObjectConverter::class);
}
return $this->extensibleDataObjectConverter;
}

/**
* Lazy loader for the metadata pool.
*
* @return \Magento\Framework\EntityManager\MetadataPool
* @return MetadataPool
*/
private function getMetadataPool()
{
if (null === $this->metadataPool) {
$this->metadataPool = \Magento\Framework\App\ObjectManager::getInstance()
->get(\Magento\Framework\EntityManager\MetadataPool::class);
$this->metadataPool = ObjectManager::getInstance()
->get(MetadataPool::class);
}
return $this->metadataPool;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Catalog\Model\CategoryRepository;

use Magento\Catalog\Api\Data\CategoryInterface;
use Magento\Catalog\Model\Attribute\ScopeOverriddenValue;
use Magento\Catalog\Model\Category;
use Magento\Catalog\Api\CategoryAttributeRepositoryInterface as AttributeRepository;
use Magento\Eav\Api\Data\AttributeInterface;
use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
use Magento\Framework\Api\FilterBuilder;
use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Store\Model\Store;

/**
* Add data to category entity and populate with default values
*/
class PopulateWithValues
{
/**
* @var ScopeOverriddenValue
*/
private $scopeOverriddenValue;

/**
* @var AttributeRepository
*/
private $attributeRepository;

/**
* @var SearchCriteriaBuilder
*/
private $searchCriteriaBuilder;

/**
* @var FilterBuilder
*/
private $filterBuilder;

/**
* @var AttributeInterface[]
*/
private $attributes;

/**
* @param ScopeOverriddenValue $scopeOverriddenValue
* @param AttributeRepository $attributeRepository
* @param SearchCriteriaBuilder $searchCriteriaBuilder
* @param FilterBuilder $filterBuilder
*/
public function __construct(
ScopeOverriddenValue $scopeOverriddenValue,
AttributeRepository $attributeRepository,
SearchCriteriaBuilder $searchCriteriaBuilder,
FilterBuilder $filterBuilder
) {
$this->scopeOverriddenValue = $scopeOverriddenValue;
$this->attributeRepository = $attributeRepository;
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
$this->filterBuilder = $filterBuilder;
}

/**
* Set null to entity default values
*
* @param CategoryInterface $category
* @param array $existingData
* @return void
*/
public function execute(CategoryInterface $category, array $existingData): void
{
$storeId = $existingData['store_id'] ?? Store::DEFAULT_STORE_ID;
if ((int)$storeId !== Store::DEFAULT_STORE_ID) {
$overriddenValues = array_filter(
$category->getData(),
function ($key) use ($category, $storeId) {
/** @var Category $category */
return $this->scopeOverriddenValue->containsValue(
CategoryInterface::class,
$category,
$key,
$storeId
);
},
ARRAY_FILTER_USE_KEY
);
$defaultValues = array_diff_key($category->getData(), $overriddenValues);
array_walk(
$defaultValues,
function (&$value, $key) {
$attributes = $this->getAttributes();
if (isset($attributes[$key]) && !$attributes[$key]->isStatic()) {
$value = null;
}
}
);
$category->addData($defaultValues);
}

$category->addData($existingData);
$useDefaultAttributes = array_filter(
$category->getData(),
function ($attributeValue) {
return null === $attributeValue;
}
);
$category->setData(
'use_default',
array_map(
function () {
return true;
},
$useDefaultAttributes
)
);
}

/**
* Returns entity attributes.
*
* @return AttributeInterface[]
*/
private function getAttributes(): array
{
if ($this->attributes) {
return $this->attributes;
}

$searchResult = $this->attributeRepository->getList(
$this->searchCriteriaBuilder->addFilters(
[
$this->filterBuilder
->setField('is_global')
->setConditionType('in')
->setValue([ScopedAttributeInterface::SCOPE_STORE, ScopedAttributeInterface::SCOPE_WEBSITE])
->create()
]
)->create()
);

$this->attributes = [];
foreach ($searchResult->getItems() as $attribute) {
$this->attributes[$attribute->getAttributeCode()] = $attribute;
}

return $this->attributes;
}
}
19 changes: 19 additions & 0 deletions app/code/Magento/Catalog/Test/Mftf/Data/NonexistentProductData.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-->

<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd">
<entity name="NonexistentProduct" type="product">
<data key="sku">NonexistentProductSku</data>
<data key="qty">1</data>
</entity>
<entity name="SecondNonexistentProduct" type="product">
<data key="sku">SecondNonexistentProductSku</data>
<data key="qty">1</data>
</entity>
</entities>
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Magento\Catalog\Model\Category as CategoryModel;
use Magento\Catalog\Model\CategoryFactory;
use Magento\Catalog\Model\CategoryRepository;
use Magento\Catalog\Model\CategoryRepository\PopulateWithValues;
use Magento\Framework\Api\ExtensibleDataObjectConverter;
use Magento\Framework\DataObject;
use Magento\Framework\EntityManager\EntityMetadata;
Expand Down Expand Up @@ -63,6 +64,14 @@ class CategoryRepositoryTest extends TestCase
*/
protected $metadataPoolMock;

/**
* @var PopulateWithValues|MockObject
*/
private $populateWithValuesMock;

/**
* @inheridoc
*/
protected function setUp(): void
{
$this->categoryFactoryMock = $this->createPartialMock(
Expand Down Expand Up @@ -94,6 +103,12 @@ protected function setUp(): void
->with(CategoryInterface::class)
->willReturn($metadataMock);

$this->populateWithValuesMock = $this
->getMockBuilder(PopulateWithValues::class)
->onlyMethods(['execute'])
->disableOriginalConstructor()
->getMock();

$this->model = (new ObjectManager($this))->getObject(
CategoryRepository::class,
[
Expand All @@ -102,6 +117,7 @@ protected function setUp(): void
'storeManager' => $this->storeManagerMock,
'metadataPool' => $this->metadataPoolMock,
'extensibleDataObjectConverter' => $this->extensibleDataObjectConverterMock,
'populateWithValues' => $this->populateWithValuesMock,
]
);
}
Expand Down Expand Up @@ -202,7 +218,7 @@ public function testFilterExtraFieldsOnUpdateCategory($categoryId, $categoryData
->method('toNestedArray')
->willReturn($categoryData);
$categoryMock->expects($this->once())->method('validate')->willReturn(true);
$categoryMock->expects($this->once())->method('addData')->with($dataForSave);
$this->populateWithValuesMock->expects($this->once())->method('execute')->with($categoryMock, $dataForSave);
$this->categoryResourceMock->expects($this->once())
->method('save')
->willReturn(DataObject::class);
Expand Down Expand Up @@ -230,11 +246,11 @@ public function testCreateNewCategory()

$categoryMock->expects($this->once())->method('getParentId')->willReturn($parentCategoryId);
$parentCategoryMock->expects($this->once())->method('getPath')->willReturn('path');
$categoryMock->expects($this->once())->method('addData')->with($dataForSave);
$categoryMock->expects($this->once())->method('validate')->willReturn(true);
$this->categoryResourceMock->expects($this->once())
->method('save')
->willReturn(DataObject::class);
$this->populateWithValuesMock->expects($this->once())->method('execute')->with($categoryMock, $dataForSave);
$this->assertEquals($categoryMock, $this->model->save($categoryMock));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
<element name="emptyMiniCart" type="text" selector="//div[@class='minicart-wrapper']//span[@class='counter qty empty']/../.."/>
<element name="minicartContent" type="block" selector="#minicart-content-wrapper"/>
<element name="messageEmptyCart" type="text" selector="//*[@id='minicart-content-wrapper']//*[contains(@class,'subtitle empty')]"/>
<element name="emptyCartMessageContent" type="text" selector="#minicart-content-wrapper .minicart.empty.text" timeout="30"/>
<element name="visibleItemsCountText" type="text" selector="//div[@class='items-total']"/>
<element name="productQuantity" type="input" selector="//*[@id='mini-cart']//a[contains(text(),'{{productName}}')]/../..//div[@class='details-qty qty']//input[@data-item-qty='{{qty}}']" parameterized="true"/>
<element name="productImage" type="text" selector="//ol[@id='mini-cart']//img[@class='product-image-photo']"/>
Expand Down
Loading

0 comments on commit 72f9180

Please sign in to comment.