From d8ea1506802f489ccfd4adddfbc107f409dc2a75 Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Fri, 5 Aug 2016 12:16:06 +0300 Subject: [PATCH 01/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../Model/Product/Link/SaveHandler.php | 16 +++++--- .../Model/Stock/StockItemRepository.php | 8 +++- .../Model/Product/VariationHandler.php | 8 +++- .../Entity/Attribute/AbstractAttribute.php | 7 ++-- .../Eav/Model/ResourceModel/CreateHandler.php | 37 +++++++++++-------- 5 files changed, 48 insertions(+), 28 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php b/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php index 3df4d9813a668..d993d3c0bc067 100644 --- a/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php @@ -40,7 +40,6 @@ public function __construct( Link $linkResource, ProductLinkRepositoryInterface $productLinkRepository ) { - $this->metadataPool = $metadataPool; $this->linkResource = $linkResource; $this->productLinkRepository = $productLinkRepository; @@ -54,12 +53,17 @@ public function __construct( */ public function execute($entityType, $entity) { - /** @var \Magento\Catalog\Api\Data\ProductInterface $entity*/ - foreach ($this->productLinkRepository->getList($entity) as $link) { - $this->productLinkRepository->delete($link); + if ($entity->getId()) { + /** @var \Magento\Catalog\Api\Data\ProductInterface $entity*/ + foreach ($this->productLinkRepository->getList($entity) as $link) { + $this->productLinkRepository->delete($link); + } } - foreach ($entity->getProductLinks() as $link) { - $this->productLinkRepository->save($link); + $productLinks = $entity->getProductLinks(); + if (count($productLinks) > 0) { + foreach ($entity->getProductLinks() as $link) { + $this->productLinkRepository->save($link); + } } return $entity; } diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php index c5cdd1950cc38..cc1025a4eefeb 100644 --- a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php +++ b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php @@ -20,8 +20,8 @@ use Magento\Framework\Exception\CouldNotDeleteException; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Framework\Stdlib\DateTime\TimezoneInterface; use Magento\Framework\Stdlib\DateTime\DateTime; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; /** * Class StockItemRepository @@ -137,7 +137,11 @@ public function save(\Magento\CatalogInventory\Api\Data\StockItemInterface $stoc try { /** @var \Magento\Catalog\Model\Product $product */ $product = $this->productFactory->create(); - $product->load($stockItem->getProductId()); + $product = $product->getCollection() + ->addIdFilter($stockItem->getProductId()) + ->addFieldToSelect('type_id') + ->getFirstItem(); + if (!$product->getId()) { return $stockItem; } diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php b/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php index 7ba330f5a060c..7ce6ad31c56d4 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php @@ -89,6 +89,7 @@ public function generateSimpleProducts($parentProduct, $productsData) $generatedProductIds[] = $newSimpleProduct->getId(); } + $this->attributes = null; return $generatedProductIds; } @@ -132,6 +133,8 @@ public function prepareAttributeSet(\Magento\Catalog\Model\Product $product) } } + protected $attributes = null; + /** * Fill simple product data during generation * @@ -156,7 +159,10 @@ protected function fillSimpleProductData( $parentProduct->getNewVariationsAttributeSetId() ); - foreach ($product->getTypeInstance()->getSetAttributes($product) as $attribute) { + if ($this->attributes === null) { + $this->attributes = $product->getTypeInstance()->getSetAttributes($product); + } + foreach ($this->attributes as $attribute) { if ($attribute->getIsUnique() || $attribute->getAttributeCode() == 'url_key' || $attribute->getFrontend()->getInputType() == 'gallery' || diff --git a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php index 4f13ee75bfe32..0d8d18df22376 100644 --- a/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php +++ b/app/code/Magento/Eav/Model/Entity/Attribute/AbstractAttribute.php @@ -6,8 +6,8 @@ namespace Magento\Eav\Model\Entity\Attribute; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Api\AttributeValueFactory; +use Magento\Framework\Exception\LocalizedException; /** * Entity/Attribute/Model - attribute abstract @@ -595,11 +595,10 @@ public function isValueEmpty($value) { /** @var array $emptyStringTypes list of attribute types that treat empty string as a possible value */ $emptyStringTypes = ['int', 'decimal', 'datetime', 'varchar', 'text', 'static']; - $attributeType = $this->getBackend()->getType(); return (is_array($value) && count($value) == 0) || $value === null - || ($value === false && $attributeType != 'int') - || ($value === '' && in_array($attributeType, $emptyStringTypes)); + || ($value === false && $this->getBackend()->getType() != 'int') + || ($value === '' && in_array($this->getBackend()->getType(), $emptyStringTypes)); } /** diff --git a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php index 1a8aaaf027f56..b87ebed74d9fa 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php +++ b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php @@ -6,9 +6,9 @@ namespace Magento\Eav\Model\ResourceModel; use Magento\Eav\Api\AttributeRepositoryInterface as AttributeRepository; -use Magento\Framework\EntityManager\Operation\AttributeInterface; -use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\EntityManager\MetadataPool; +use Magento\Framework\EntityManager\Operation\AttributeInterface; use Magento\Framework\Model\Entity\ScopeResolver; /** @@ -70,14 +70,19 @@ public function __construct( */ protected function getAttributes($entityType) { - $metadata = $this->metadataPool->getMetadata($entityType); - $searchResult = $this->attributeRepository->getList( - $metadata->getEavEntityType(), - $this->searchCriteriaBuilder->addFilter('attribute_set_id', null, 'neq')->create() - ); - return $searchResult->getItems(); + if (!isset($this->attributes[$entityType])) { + $metadata = $this->metadataPool->getMetadata($entityType); + $searchResult = $this->attributeRepository->getList( + $metadata->getEavEntityType(), + $this->searchCriteriaBuilder->addFilter('attribute_set_id', null, 'neq')->create() + ); + $this->attributes[$entityType] = $searchResult->getItems(); + } + return $this->attributes[$entityType]; } + private $attributes = []; + /** * @param string $entityType * @param array $entityData @@ -92,23 +97,25 @@ public function execute($entityType, $entityData, $arguments = []) $metadata = $this->metadataPool->getMetadata($entityType); if ($metadata->getEavEntityType()) { $processed = []; + $entityLinkField = $metadata->getLinkField(); /** @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute */ foreach ($this->getAttributes($entityType) as $attribute) { if ($attribute->isStatic()) { continue; } - if (isset($entityData[$attribute->getAttributeCode()]) - && !is_array($entityData[$attribute->getAttributeCode()]) - && !$attribute->isValueEmpty($entityData[$attribute->getAttributeCode()]) + + $attributeCode = $attribute->getAttributeCode(); + if (isset($entityData[$attributeCode]) + && !is_array($entityData[$attributeCode]) + && !$attribute->isValueEmpty($entityData[$attributeCode]) ) { - $entityLinkField = $metadata->getLinkField(); $this->attributePersistor->registerInsert( $entityType, $entityData[$entityLinkField], - $attribute->getAttributeCode(), - $entityData[$attribute->getAttributeCode()] + $attributeCode, + $entityData[$attributeCode] ); - $processed[$attribute->getAttributeCode()] = $entityData[$attribute->getAttributeCode()]; + $processed[$attributeCode] = $entityData[$attributeCode]; } } $context = $this->scopeResolver->getEntityContext($entityType, $entityData); From bb1e6935435919425fcfc7e4d7c686a424c31769 Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Thu, 15 Sep 2016 16:31:20 +0300 Subject: [PATCH 02/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../Model/Product/Attribute/Backend/Sku.php | 5 +- .../Model/Product/Gallery/CreateHandler.php | 22 ++++++++- .../Model/Product/Link/SaveHandler.php | 2 +- .../Model/Product/Option/CreateHandler.php | 46 +++++++++++++++++++ .../Model/ResourceModel/Eav/Attribute.php | 11 ++--- .../Model/ResourceModel/Product/Link.php | 24 ++++++++++ app/code/Magento/Catalog/etc/di.xml | 2 +- .../ResourceModel/AttributePersistor.php | 9 ++-- .../Eav/Model/ResourceModel/UpdateHandler.php | 16 +++++++ 9 files changed, 122 insertions(+), 15 deletions(-) create mode 100644 app/code/Magento/Catalog/Model/Product/Option/CreateHandler.php diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php index 327729cf83695..290770bd296c3 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/Sku.php @@ -73,9 +73,12 @@ protected function _generateUniqueSku($object) { $attribute = $this->getAttribute(); $entity = $attribute->getEntity(); - $increment = $this->_getLastSimilarAttributeValueIncrement($attribute, $object); $attributeValue = $object->getData($attribute->getAttributeCode()); + $increment = null; while (!$entity->checkAttributeUniqueValue($attribute, $object)) { + if ($increment === null) { + $increment = $this->_getLastSimilarAttributeValueIncrement($attribute, $object); + } $sku = trim($attributeValue); if (strlen($sku . '-' . ++$increment) > self::SKU_MAX_LENGTH) { $sku = substr($sku, 0, -strlen($increment) - 1); diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php index ada156f571b0e..471913dc4888b 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php @@ -58,6 +58,9 @@ class CreateHandler implements ExtensionInterface */ protected $fileStorageDb; + /** @var array */ + private $mediaAttributeCodes; + /** * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository @@ -145,9 +148,11 @@ public function execute($product, $arguments = []) } /* @var $mediaAttribute \Magento\Catalog\Api\Data\ProductAttributeInterface */ - foreach ($this->mediaConfig->getMediaAttributeCodes() as $mediaAttrCode) { + foreach ($this->getMediaAttributeCodes() as $mediaAttrCode) { $attrData = $product->getData($mediaAttrCode); - + if (empty($attrData) && empty($clearImages) && empty($newImages) && empty($existImages)) { + continue; + } if (in_array($attrData, $clearImages)) { $product->setData($mediaAttrCode, 'no_selection'); } @@ -393,4 +398,17 @@ protected function copyImage($file) ); } } + + /** + * Get Media Attribute Codes cached value + * + * @return array + */ + private function getMediaAttributeCodes() + { + if ($this->mediaAttributeCodes === null) { + $this->mediaAttributeCodes = $this->mediaConfig->getMediaAttributeCodes(); + } + return $this->mediaAttributeCodes; + } } diff --git a/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php b/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php index d993d3c0bc067..85a54e7fcb55a 100644 --- a/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php @@ -53,7 +53,7 @@ public function __construct( */ public function execute($entityType, $entity) { - if ($entity->getId()) { + if ($this->linkResource->hasProductLinks($entity)) { /** @var \Magento\Catalog\Api\Data\ProductInterface $entity*/ foreach ($this->productLinkRepository->getList($entity) as $link) { $this->productLinkRepository->delete($link); diff --git a/app/code/Magento/Catalog/Model/Product/Option/CreateHandler.php b/app/code/Magento/Catalog/Model/Product/Option/CreateHandler.php new file mode 100644 index 0000000000000..392689b39ff9e --- /dev/null +++ b/app/code/Magento/Catalog/Model/Product/Option/CreateHandler.php @@ -0,0 +1,46 @@ +optionRepository = $optionRepository; + } + + /** + * @param object $entity + * @param array $arguments + * @return \Magento\Catalog\Api\Data\ProductInterface|object + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function execute($entity, $arguments = []) + { + if ($entity->getOptions()) { + foreach ($entity->getOptions() as $option) { + $this->optionRepository->save($option); + } + } + return $entity; + } +} diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php index 99c4316d20740..8e93868dc7e51 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php @@ -351,14 +351,11 @@ public function getStoreId() */ public function getApplyTo() { - if ($this->getData(self::APPLY_TO)) { - if (is_array($this->getData(self::APPLY_TO))) { - return $this->getData(self::APPLY_TO); - } - return explode(',', $this->getData(self::APPLY_TO)); - } else { - return []; + $applyTo = $this->_getData(self::APPLY_TO) ?: []; + if (!is_array($applyTo)) { + $applyTo = explode(',', $applyTo); } + return $applyTo; } /** diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php index bbccabe5a61c9..dd984adaf8a3d 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php @@ -96,6 +96,30 @@ public function getProductLinkId($parentId, $linkedProductId, $typeId) return $connection->fetchOne($select, $bind); } + /** + * Is product has links + * + * @param $entity + * @return int + */ + public function hasProductLinks($entity) + { + $connection = $this->getConnection(); + $select = $connection->select()->from( + $this->getMainTable(), + ['count' => new \Zend_Db_Expr('COUNT(*)')] + )->where( + 'product_id = :product_id' + ) ; + + return $connection->fetchOne( + $select, + [ + 'product_id' => $entity->getId() + ] + ) > 0; + } + /** * Save Product Links process * diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 49ad7d67d7058..bb8cf5c1ac70e 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -606,7 +606,7 @@ Magento\Catalog\Model\Product\Website\ReadHandler - Magento\Catalog\Model\Product\Option\SaveHandler + Magento\Catalog\Model\Product\Option\CreateHandler Magento\Catalog\Model\Product\Gallery\CreateHandler Magento\Catalog\Model\Category\Link\SaveHandler Magento\Catalog\Model\Product\Website\SaveHandler diff --git a/app/code/Magento/Eav/Model/ResourceModel/AttributePersistor.php b/app/code/Magento/Eav/Model/ResourceModel/AttributePersistor.php index 74373cb593c40..5e340595df986 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/AttributePersistor.php +++ b/app/code/Magento/Eav/Model/ResourceModel/AttributePersistor.php @@ -113,13 +113,14 @@ public function processDeletes($entityType, $context) return; } $metadata = $this->metadataPool->getMetadata($entityType); + $linkField = $metadata->getLinkField(); foreach ($this->delete[$entityType] as $link => $data) { $attributeCodes = array_keys($data); foreach ($attributeCodes as $attributeCode) { /** @var AbstractAttribute $attribute */ $attribute = $this->attributeRepository->get($metadata->getEavEntityType(), $attributeCode); $conditions = [ - $metadata->getLinkField() . ' = ?' => $link, + $linkField . ' = ?' => $link, 'attribute_id = ?' => $attribute->getAttributeId() ]; foreach ($context as $scope) { @@ -147,6 +148,7 @@ public function processInserts($entityType, $context) return; } $metadata = $this->metadataPool->getMetadata($entityType); + $linkField = $metadata->getLinkField(); foreach ($this->insert[$entityType] as $link => $data) { foreach ($data as $attributeCode => $attributeValue) { /** @var AbstractAttribute $attribute */ @@ -155,7 +157,7 @@ public function processInserts($entityType, $context) $attributeCode ); $data = [ - $metadata->getLinkField() => $link, + $linkField => $link, 'attribute_id' => $attribute->getAttributeId(), 'value' => $this->prepareValue($entityType, $attributeValue, $attribute) ]; @@ -180,6 +182,7 @@ public function processUpdates($entityType, $context) return; } $metadata = $this->metadataPool->getMetadata($entityType); + $linkField = $metadata->getLinkField(); foreach ($this->update[$entityType] as $link => $data) { foreach ($data as $attributeCode => $attributeValue) { /** @var AbstractAttribute $attribute */ @@ -188,7 +191,7 @@ public function processUpdates($entityType, $context) $attributeCode ); $conditions = [ - $metadata->getLinkField() . ' = ?' => $link, + $linkField . ' = ?' => $link, 'attribute_id = ?' => $attribute->getAttributeId(), ]; foreach ($context as $scope) { diff --git a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php index 0f892a272fa1a..5196ca4c338e4 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php +++ b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php @@ -79,6 +79,16 @@ public function __construct( $this->scopeResolver = $scopeResolver; } + /** + * @return \Magento\Eav\Model\Entity\AttributeCache + + */ + private function getAttributeCache() + { + return ObjectManager::getInstance()->get(\Magento\Eav\Model\Entity\AttributeCache::class); + } + + /** * @param string $entityType * @return \Magento\Eav\Api\Data\AttributeInterface[] @@ -86,6 +96,12 @@ public function __construct( */ protected function getAttributes($entityType) { + /** @var \Magento\Eav\Model\Entity\AttributeCache $cache */ + $cache = $this->getAttributeCache(); + if ($attributes = $cache->getAttributes($entityType)) { + return $attributes; + } + $metadata = $this->metadataPool->getMetadata($entityType); $searchResult = $this->attributeRepository->getList( From 125efd857b3152b609f23b6ec469210cf23d3ce8 Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Fri, 16 Sep 2016 11:42:34 +0300 Subject: [PATCH 03/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../Model/Product/Link/SaveHandler.php | 2 +- .../Model/ResourceModel/Product/Link.php | 6 ++--- .../Model/Stock/StockItemRepository.php | 22 ++++++++++++++-- .../Model/Stock/StockItemRepositoryTest.php | 25 ++++++++++++++----- .../Eav/Model/ResourceModel/CreateHandler.php | 7 ++++-- .../Eav/Model/ResourceModel/UpdateHandler.php | 3 +-- .../Attribute/AbstractAttributeTest.php | 2 +- 7 files changed, 50 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php b/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php index 85a54e7fcb55a..cfa1805e859d3 100644 --- a/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php @@ -53,7 +53,7 @@ public function __construct( */ public function execute($entityType, $entity) { - if ($this->linkResource->hasProductLinks($entity)) { + if ($this->linkResource->hasProductLinks($entity->getId())) { /** @var \Magento\Catalog\Api\Data\ProductInterface $entity*/ foreach ($this->productLinkRepository->getList($entity) as $link) { $this->productLinkRepository->delete($link); diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php index dd984adaf8a3d..2af9b51fde969 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php @@ -99,10 +99,10 @@ public function getProductLinkId($parentId, $linkedProductId, $typeId) /** * Is product has links * - * @param $entity + * @param int $parentId * @return int */ - public function hasProductLinks($entity) + public function hasProductLinks($parentId) { $connection = $this->getConnection(); $select = $connection->select()->from( @@ -115,7 +115,7 @@ public function hasProductLinks($entity) return $connection->fetchOne( $select, [ - 'product_id' => $entity->getId() + 'product_id' => $parentId ] ) > 0; } diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php index cc1025a4eefeb..102210caae17e 100644 --- a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php +++ b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php @@ -15,6 +15,7 @@ use Magento\CatalogInventory\Model\ResourceModel\Stock\Item as StockItemResource; use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface; use Magento\CatalogInventory\Model\StockRegistryStorage; +use Magento\Framework\App\ObjectManager; use Magento\Framework\DB\MapperFactory; use Magento\Framework\DB\QueryBuilderFactory; use Magento\Framework\Exception\CouldNotDeleteException; @@ -89,6 +90,9 @@ class StockItemRepository implements StockItemRepositoryInterface */ protected $stockRegistryStorage; + /** @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory */ + protected $productCollectionFactory; + /** * @param StockConfigurationInterface $stockConfiguration * @param StockStateProviderInterface $stockStateProvider @@ -129,6 +133,21 @@ public function __construct( $this->dateTime = $dateTime; } + /** + * @return \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory + */ + private function getProductCollectionFactory() + { + if ($this->productCollectionFactory === null) { + $this->productCollectionFactory = ObjectManager::getInstance()->get( + \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory::class + ); + } + + return $this->productCollectionFactory; + } + + /** * @inheritdoc */ @@ -136,8 +155,7 @@ public function save(\Magento\CatalogInventory\Api\Data\StockItemInterface $stoc { try { /** @var \Magento\Catalog\Model\Product $product */ - $product = $this->productFactory->create(); - $product = $product->getCollection() + $product = $this->getProductCollectionFactory()->create() ->addIdFilter($stockItem->getProductId()) ->addFieldToSelect('type_id') ->getFirstItem(); diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php index 769e2db4cc845..9a8cb35b83a15 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php @@ -5,6 +5,7 @@ */ namespace Magento\CatalogInventory\Test\Unit\Model\Stock; +use Magento\Catalog\Model\ResourceModel\Product\Collection; use Magento\CatalogInventory\Model\Stock\StockItemRepository; use Magento\CatalogInventory\Api\Data as InventoryApiData; use Magento\CatalogInventory\Model\StockRegistryStorage; @@ -92,6 +93,9 @@ class StockItemRepositoryTest extends \PHPUnit_Framework_TestCase */ protected $stockRegistryStorage; + /** @var Collection */ + protected $productCollection; + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -153,9 +157,6 @@ protected function setUp() ->disableOriginalConstructor() ->setMethods(['load', 'getId', 'getTypeId', '__wakeup']) ->getMock(); - $this->productFactoryMock->expects($this->any()) - ->method('create') - ->willReturn($this->productMock); $this->queryBuilderFactoryMock = $this->getMockBuilder(\Magento\Framework\DB\QueryBuilderFactory::class) ->setMethods(['create']) @@ -185,6 +186,20 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); + $this->productCollection = $this->getMockBuilder( + \Magento\Catalog\Model\ResourceModel\Product\Collection::class + )->disableOriginalConstructor()->getMock(); + + $this->productCollection->expects($this->any())->method('addIdFilter')->willReturnSelf(); + $this->productCollection->expects($this->any())->method('addFieldToSelect')->willReturnSelf(); + $this->productCollection->expects($this->any())->method('getFirstItem')->willReturn($this->productMock); + + $productCollectionFactory = $this->getMockBuilder( + \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory::class + )->disableOriginalConstructor()->getMock(); + + $productCollectionFactory->expects($this->any())->method('create')->willReturn($this->productCollection); + $this->model = (new ObjectManager($this))->getObject( StockItemRepository::class, [ @@ -200,6 +215,7 @@ protected function setUp() 'indexProcessor' => $this->indexProcessorMock, 'dateTime' => $this->dateTime, 'stockRegistryStorage' => $this->stockRegistryStorage, + 'productCollectionFactory' => $productCollectionFactory, ] ); } @@ -263,7 +279,6 @@ public function testSave() $productId = 1; $this->stockItemMock->expects($this->any())->method('getProductId')->willReturn($productId); - $this->productMock->expects($this->once())->method('load')->with($productId)->willReturnSelf(); $this->productMock->expects($this->once())->method('getId')->willReturn($productId); $this->productMock->expects($this->once())->method('getTypeId')->willReturn('typeId'); $this->stockConfigurationMock->expects($this->once())->method('isQty')->with('typeId')->willReturn(true); @@ -309,7 +324,6 @@ public function testSaveWithoutProductId() $productId = 1; $this->stockItemMock->expects($this->any())->method('getProductId')->willReturn($productId); - $this->productMock->expects($this->once())->method('load')->with($productId)->willReturnSelf(); $this->productMock->expects($this->once())->method('getId')->willReturn(null); $this->stockRegistryStorage->expects($this->never())->method('removeStockItem'); $this->stockRegistryStorage->expects($this->never())->method('removeStockStatus'); @@ -325,7 +339,6 @@ public function testSaveException() $productId = 1; $this->stockItemMock->expects($this->any())->method('getProductId')->willReturn($productId); - $this->productMock->expects($this->once())->method('load')->with($productId)->willReturnSelf(); $this->productMock->expects($this->once())->method('getId')->willReturn($productId); $this->productMock->expects($this->once())->method('getTypeId')->willReturn('typeId'); $this->stockConfigurationMock->expects($this->once())->method('isQty')->with('typeId')->willReturn(false); diff --git a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php index b87ebed74d9fa..72684655d099e 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php +++ b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php @@ -42,6 +42,11 @@ class CreateHandler implements AttributeInterface */ private $scopeResolver; + /** + * @var array + */ + private $attributes = []; + /** * @param AttributeRepository $attributeRepository * @param MetadataPool $metadataPool @@ -81,8 +86,6 @@ protected function getAttributes($entityType) return $this->attributes[$entityType]; } - private $attributes = []; - /** * @param string $entityType * @param array $entityData diff --git a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php index 5196ca4c338e4..c917a82b6701f 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php +++ b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php @@ -80,15 +80,14 @@ public function __construct( } /** + * @deprecated * @return \Magento\Eav\Model\Entity\AttributeCache - */ private function getAttributeCache() { return ObjectManager::getInstance()->get(\Magento\Eav\Model\Entity\AttributeCache::class); } - /** * @param string $entityType * @return \Magento\Eav\Api\Data\AttributeInterface[] diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/AbstractAttributeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/AbstractAttributeTest.php index d6b88e0ac5691..a10bafacda42d 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/AbstractAttributeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Attribute/AbstractAttributeTest.php @@ -207,7 +207,7 @@ public function testIsValueEmpty($isEmpty, $value, $attributeType) ] ); $backendModelMock->expects($this->any())->method('getType')->willReturn($attributeType); - $model->expects($this->once())->method('getBackend')->willReturn($backendModelMock); + $model->expects($this->any())->method('getBackend')->willReturn($backendModelMock); $this->assertEquals($isEmpty, $model->isValueEmpty($value)); } From 487eb75dc71f94e76eb1bb8829ccb4424656261b Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Fri, 16 Sep 2016 12:51:51 +0300 Subject: [PATCH 04/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../Model/Stock/StockItemRepository.php | 1 - .../Model/Stock/StockItemRepositoryTest.php | 19 +++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php index 102210caae17e..906bb08d71828 100644 --- a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php +++ b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php @@ -147,7 +147,6 @@ private function getProductCollectionFactory() return $this->productCollectionFactory; } - /** * @inheritdoc */ diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php index 9a8cb35b83a15..3f7103eb459da 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php @@ -6,6 +6,7 @@ namespace Magento\CatalogInventory\Test\Unit\Model\Stock; use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory; use Magento\CatalogInventory\Model\Stock\StockItemRepository; use Magento\CatalogInventory\Api\Data as InventoryApiData; use Magento\CatalogInventory\Model\StockRegistryStorage; @@ -93,9 +94,6 @@ class StockItemRepositoryTest extends \PHPUnit_Framework_TestCase */ protected $stockRegistryStorage; - /** @var Collection */ - protected $productCollection; - /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -186,17 +184,18 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $this->productCollection = $this->getMockBuilder( + $productCollection = $this->getMockBuilder( \Magento\Catalog\Model\ResourceModel\Product\Collection::class )->disableOriginalConstructor()->getMock(); - $this->productCollection->expects($this->any())->method('addIdFilter')->willReturnSelf(); - $this->productCollection->expects($this->any())->method('addFieldToSelect')->willReturnSelf(); - $this->productCollection->expects($this->any())->method('getFirstItem')->willReturn($this->productMock); + $productCollection->expects($this->any())->method('addIdFilter')->willReturnSelf(); + $productCollection->expects($this->any())->method('addFieldToSelect')->willReturnSelf(); + $productCollection->expects($this->any())->method('getFirstItem')->willReturn($this->productMock); - $productCollectionFactory = $this->getMockBuilder( - \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory::class - )->disableOriginalConstructor()->getMock(); + $productCollectionFactory = $this->getMockBuilder(CollectionFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); $productCollectionFactory->expects($this->any())->method('create')->willReturn($this->productCollection); From 3a0ccd82f5f544921a3008db2b660cf451a2105a Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Fri, 16 Sep 2016 15:04:01 +0300 Subject: [PATCH 05/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../Test/Unit/Model/Stock/StockItemRepositoryTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php index 3f7103eb459da..d7efcf11e8057 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php @@ -197,7 +197,7 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); - $productCollectionFactory->expects($this->any())->method('create')->willReturn($this->productCollection); + $productCollectionFactory->expects($this->any())->method('create')->willReturn($productCollection); $this->model = (new ObjectManager($this))->getObject( StockItemRepository::class, From e7878028f25560c7c77ed5a58b8df93b51e3cae7 Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Mon, 19 Sep 2016 14:56:20 +0300 Subject: [PATCH 06/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php b/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php index cfa1805e859d3..167dc2be15b29 100644 --- a/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php @@ -53,7 +53,8 @@ public function __construct( */ public function execute($entityType, $entity) { - if ($this->linkResource->hasProductLinks($entity->getId())) { + $link = $entity->getData($this->metadataPool->getMetadata($entityType)->getLinkField()); + if ($this->linkResource->hasProductLinks($link)) { /** @var \Magento\Catalog\Api\Data\ProductInterface $entity*/ foreach ($this->productLinkRepository->getList($entity) as $link) { $this->productLinkRepository->delete($link); From e8033701f06a4c08f5ad72406999c99f2ba9dc18 Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Tue, 20 Sep 2016 11:11:08 +0300 Subject: [PATCH 07/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../Eav/Model/ResourceModel/CreateHandler.php | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php index 72684655d099e..f72bba39d6e29 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php +++ b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php @@ -7,6 +7,7 @@ use Magento\Eav\Api\AttributeRepositoryInterface as AttributeRepository; use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\App\ObjectManager; use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\EntityManager\Operation\AttributeInterface; use Magento\Framework\Model\Entity\ScopeResolver; @@ -42,11 +43,6 @@ class CreateHandler implements AttributeInterface */ private $scopeResolver; - /** - * @var array - */ - private $attributes = []; - /** * @param AttributeRepository $attributeRepository * @param MetadataPool $metadataPool @@ -68,6 +64,15 @@ public function __construct( $this->scopeResolver = $scopeResolver; } + /** + * @deprecated + * @return \Magento\Eav\Model\Entity\AttributeCache + */ + private function getAttributeCache() + { + return ObjectManager::getInstance()->get(\Magento\Eav\Model\Entity\AttributeCache::class); + } + /** * @param string $entityType * @return \Magento\Eav\Api\Data\AttributeInterface[] @@ -75,15 +80,19 @@ public function __construct( */ protected function getAttributes($entityType) { - if (!isset($this->attributes[$entityType])) { - $metadata = $this->metadataPool->getMetadata($entityType); - $searchResult = $this->attributeRepository->getList( - $metadata->getEavEntityType(), - $this->searchCriteriaBuilder->addFilter('attribute_set_id', null, 'neq')->create() - ); - $this->attributes[$entityType] = $searchResult->getItems(); + /** @var \Magento\Eav\Model\Entity\AttributeCache $cache */ + $cache = $this->getAttributeCache(); + if ($attributes = $cache->getAttributes($entityType)) { + return $attributes; } - return $this->attributes[$entityType]; + + $metadata = $this->metadataPool->getMetadata($entityType); + + $searchResult = $this->attributeRepository->getList( + $metadata->getEavEntityType(), + $this->searchCriteriaBuilder->addFilter('attribute_set_id', null, 'neq')->create() + ); + return $searchResult->getItems(); } /** From cc34934d17ab06ca98c61e51b9f5b8035b051529 Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Tue, 20 Sep 2016 14:00:02 +0300 Subject: [PATCH 08/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../CatalogInventory/Model/Stock/StockItemRepository.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php index 906bb08d71828..7858d3cc0cb08 100644 --- a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php +++ b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php @@ -154,10 +154,8 @@ public function save(\Magento\CatalogInventory\Api\Data\StockItemInterface $stoc { try { /** @var \Magento\Catalog\Model\Product $product */ - $product = $this->getProductCollectionFactory()->create() - ->addIdFilter($stockItem->getProductId()) - ->addFieldToSelect('type_id') - ->getFirstItem(); + $product = $this->productFactory->create(); + $product->load($stockItem->getProductId()); if (!$product->getId()) { return $stockItem; From cb816f9eeedb000fbe8bf9ef4d7263e4ab0029fd Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Thu, 22 Sep 2016 16:40:48 +0300 Subject: [PATCH 09/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../Magento/Catalog/Model/Product/Option/CreateHandler.php | 5 +++++ .../ConfigurableProduct/Model/Product/VariationHandler.php | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Option/CreateHandler.php b/app/code/Magento/Catalog/Model/Product/Option/CreateHandler.php index 392689b39ff9e..3744124de034f 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/CreateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Option/CreateHandler.php @@ -36,6 +36,11 @@ public function __construct( */ public function execute($entity, $arguments = []) { + /** @var \Magento\Catalog\Api\Data\ProductInterface $entity */ + foreach ($this->optionRepository->getProductOptions($entity) as $option) { + $this->optionRepository->delete($option); + } + if ($entity->getOptions()) { foreach ($entity->getOptions() as $option) { $this->optionRepository->save($option); diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php b/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php index 51435b8af6c1c..733e382bccf46 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php @@ -29,6 +29,9 @@ class VariationHandler /** @var \Magento\Catalog\Model\ProductFactory */ protected $productFactory; + /** @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute[] */ + private $attributes; + /** * @var \Magento\CatalogInventory\Api\StockConfigurationInterface * @deprecated @@ -133,8 +136,6 @@ public function prepareAttributeSet(\Magento\Catalog\Model\Product $product) } } - protected $attributes = null; - /** * Fill simple product data during generation * From 729e74424d3bd4e5b9b13ead91f41f4c798a395b Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Thu, 22 Sep 2016 19:53:27 +0300 Subject: [PATCH 10/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-56986: Save is executed only for updated data --- .../Initialization/Helper/Plugin/UpdateConfigurations.php | 8 +++++++- .../web/js/components/dynamic-rows-configurable.js | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php index ea24235b0fe2e..4d282c409342a 100644 --- a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php +++ b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php @@ -32,7 +32,8 @@ class UpdateConfigurations 'swatch_image', 'small_image', 'thumbnail', - 'image' + 'image', + 'need_update' ]; /** @@ -66,6 +67,11 @@ public function afterInitialize( $configurations = $this->getConfigurations(); $configurations = $this->variationHandler->duplicateImagesForVariations($configurations); foreach ($configurations as $productId => $productData) { + if (empty($productData['need_update'])) { + continue; + } else { + unset($productData['need_update']); + } /** @var \Magento\Catalog\Model\Product $product */ $product = $this->productRepository->getById($productId, false, $this->request->getParam('store', 0)); $productData = $this->variationHandler->processMediaGallery($product, $productData); diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js index ffabd9a8627df..ca47067b818d2 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js @@ -391,7 +391,8 @@ define([ 'small_image': row['small_image'], image: row.image, 'thumbnail': row.thumbnail, - 'attributes': attributesText + 'attributes': attributesText, + 'need_update': true }; product[this.canEditField] = row.editable; product[this.newProductField] = row.newProduct; From f62fd3226d5d72d32bc6af6f8b360f25ad614205 Mon Sep 17 00:00:00 2001 From: Andrey Dimov Date: Fri, 23 Sep 2016 12:00:13 +0300 Subject: [PATCH 11/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-56986: Save is executed only for updated data --- .../Model/Stock/StockItemRepository.php | 17 ----------------- .../Model/Stock/StockItemRepositoryTest.php | 2 ++ 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php index 7858d3cc0cb08..050d245bf949d 100644 --- a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php +++ b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php @@ -90,9 +90,6 @@ class StockItemRepository implements StockItemRepositoryInterface */ protected $stockRegistryStorage; - /** @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory */ - protected $productCollectionFactory; - /** * @param StockConfigurationInterface $stockConfiguration * @param StockStateProviderInterface $stockStateProvider @@ -133,20 +130,6 @@ public function __construct( $this->dateTime = $dateTime; } - /** - * @return \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory - */ - private function getProductCollectionFactory() - { - if ($this->productCollectionFactory === null) { - $this->productCollectionFactory = ObjectManager::getInstance()->get( - \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory::class - ); - } - - return $this->productCollectionFactory; - } - /** * @inheritdoc */ diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php index d7efcf11e8057..2f9f8daac63df 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php @@ -156,6 +156,8 @@ protected function setUp() ->setMethods(['load', 'getId', 'getTypeId', '__wakeup']) ->getMock(); + $this->productFactoryMock->expects($this->any())->method('create')->willReturn($this->productMock); + $this->queryBuilderFactoryMock = $this->getMockBuilder(\Magento\Framework\DB\QueryBuilderFactory::class) ->setMethods(['create']) ->disableOriginalConstructor() From 392aabc865bba5ef1df8a1be74b5a1d761fc3e1d Mon Sep 17 00:00:00 2001 From: Andrey Dimov Date: Mon, 26 Sep 2016 12:26:17 +0300 Subject: [PATCH 12/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-56986: Save is executed only for updated data --- .../Helper/Plugin/UpdateConfigurations.php | 12 ++++++------ .../web/js/components/dynamic-rows-configurable.js | 3 +-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php index 4d282c409342a..93b8c3231f5f3 100644 --- a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php +++ b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php @@ -33,7 +33,6 @@ class UpdateConfigurations 'small_image', 'thumbnail', 'image', - 'need_update' ]; /** @@ -67,11 +66,6 @@ public function afterInitialize( $configurations = $this->getConfigurations(); $configurations = $this->variationHandler->duplicateImagesForVariations($configurations); foreach ($configurations as $productId => $productData) { - if (empty($productData['need_update'])) { - continue; - } else { - unset($productData['need_update']); - } /** @var \Magento\Catalog\Model\Product $product */ $product = $this->productRepository->getById($productId, false, $this->request->getParam('store', 0)); $productData = $this->variationHandler->processMediaGallery($product, $productData); @@ -97,6 +91,12 @@ protected function getConfigurations() } foreach ($configurableMatrix as $item) { + if (empty($item['was_changed'])) { + continue; + } else { + unset($item['was_changed']); + } + if (!$item['newProduct']) { $result[$item['id']] = $this->mapData($item); diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js index ca47067b818d2..c182d9f8216c0 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/web/js/components/dynamic-rows-configurable.js @@ -392,11 +392,10 @@ define([ image: row.image, 'thumbnail': row.thumbnail, 'attributes': attributesText, - 'need_update': true + 'was_changed': true }; product[this.canEditField] = row.editable; product[this.newProductField] = row.newProduct; - tmpArray.push(product); }, this); From 278b42e15a0fe4d594e1ecbf3163c9def9e77252 Mon Sep 17 00:00:00 2001 From: Andrey Dimov Date: Mon, 26 Sep 2016 14:19:36 +0300 Subject: [PATCH 13/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-56986: Save is executed only for updated data --- .../Helper/Plugin/UpdateConfigurations.php | 16 +++--- .../Plugin/UpdateConfigurationsTest.php | 54 ++++++++++++------- 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php index 93b8c3231f5f3..cb9a6b534609f 100644 --- a/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php +++ b/app/code/Magento/ConfigurableProduct/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurations.php @@ -65,13 +65,15 @@ public function afterInitialize( ) { $configurations = $this->getConfigurations(); $configurations = $this->variationHandler->duplicateImagesForVariations($configurations); - foreach ($configurations as $productId => $productData) { - /** @var \Magento\Catalog\Model\Product $product */ - $product = $this->productRepository->getById($productId, false, $this->request->getParam('store', 0)); - $productData = $this->variationHandler->processMediaGallery($product, $productData); - $product->addData($productData); - if ($product->hasDataChanges()) { - $product->save(); + if (count($configurations)) { + foreach ($configurations as $productId => $productData) { + /** @var \Magento\Catalog\Model\Product $product */ + $product = $this->productRepository->getById($productId, false, $this->request->getParam('store', 0)); + $productData = $this->variationHandler->processMediaGallery($product, $productData); + $product->addData($productData); + if ($product->hasDataChanges()) { + $product->save(); + } } } return $configurableProduct; diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurationsTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurationsTest.php index bed5c96b5216e..ef6505110c7a7 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurationsTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurationsTest.php @@ -90,13 +90,30 @@ public function testAfterInitialize() 'swatch_image' => 'simple2_swatch_image', 'small_image' => 'simple2_small_image', 'thumbnail' => 'simple2_thumbnail', - 'image' => 'simple2_image' + 'image' => 'simple2_image', + 'was_changed' => true, ], [ 'newProduct' => false, 'id' => 'product3', - 'qty' => '3' - ] + 'qty' => '3', + 'was_changed' => true, + ], + [ + 'newProduct' => false, + 'id' => 'product4', + 'status' => 'simple4_status', + 'sku' => 'simple2_sku', + 'name' => 'simple2_name', + 'price' => '3.33', + 'configurable_attribute' => 'simple2_configurable_attribute', + 'weight' => '5.55', + 'media_gallery' => 'simple4_media_gallery', + 'swatch_image' => 'simple4_swatch_image', + 'small_image' => 'simple4_small_image', + 'thumbnail' => 'simple4_thumbnail', + 'image' => 'simple4_image' + ], ]; $configurations = [ 'product2' => [ @@ -118,8 +135,8 @@ public function testAfterInitialize() ]; /** @var Product[]|\PHPUnit_Framework_MockObject_MockObject[] $productMocks */ $productMocks = [ - 'product2' => $this->getProductMock($configurations['product2'], true), - 'product3' => $this->getProductMock($configurations['product3']) + 'product2' => $this->getProductMock($configurations['product2'], true, true), + 'product3' => $this->getProductMock($configurations['product3'], false, true), ]; $this->requestMock->expects(static::any()) @@ -161,26 +178,27 @@ public function testAfterInitialize() * @param bool $hasDataChanges * @return Product|\PHPUnit_Framework_MockObject_MockObject */ - protected function getProductMock(array $expectedData = null, $hasDataChanges = false) + protected function getProductMock(array $expectedData = null, $hasDataChanges = false, $wasChanged = false) { $productMock = $this->getMockBuilder(Product::class) ->disableOriginalConstructor() ->getMock(); - if ($expectedData !== null) { - $productMock->expects(static::once()) - ->method('addData') - ->with($expectedData) + if ($wasChanged !== false) { + if ($expectedData !== null) { + $productMock->expects(static::once()) + ->method('addData') + ->with($expectedData) + ->willReturnSelf(); + } + + $productMock->expects(static::any()) + ->method('hasDataChanges') + ->willReturn($hasDataChanges); + $productMock->expects($hasDataChanges ? static::once() : static::never()) + ->method('save') ->willReturnSelf(); } - - $productMock->expects(static::any()) - ->method('hasDataChanges') - ->willReturn($hasDataChanges); - $productMock->expects($hasDataChanges ? static::once() : static::never()) - ->method('save') - ->willReturnSelf(); - return $productMock; } } From 0c7e5de1e746f2a6e4ecee23bab2731d53e87a9f Mon Sep 17 00:00:00 2001 From: Andrey Dimov Date: Mon, 26 Sep 2016 15:35:37 +0300 Subject: [PATCH 14/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-56986: Save is executed only for updated data --- .../Helper/Plugin/UpdateConfigurationsTest.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurationsTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurationsTest.php index ef6505110c7a7..def49f42fa960 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurationsTest.php +++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Controller/Adminhtml/Product/Initialization/Helper/Plugin/UpdateConfigurationsTest.php @@ -106,13 +106,7 @@ public function testAfterInitialize() 'sku' => 'simple2_sku', 'name' => 'simple2_name', 'price' => '3.33', - 'configurable_attribute' => 'simple2_configurable_attribute', 'weight' => '5.55', - 'media_gallery' => 'simple4_media_gallery', - 'swatch_image' => 'simple4_swatch_image', - 'small_image' => 'simple4_small_image', - 'thumbnail' => 'simple4_thumbnail', - 'image' => 'simple4_image' ], ]; $configurations = [ From 9f066d44808c9555b1b8f9ab3ecff4bb186b0c15 Mon Sep 17 00:00:00 2001 From: Andrey Dimov Date: Mon, 26 Sep 2016 17:14:19 +0300 Subject: [PATCH 15/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-56986: Save is executed only for updated data --- .../Model/Product/Option/CreateHandler.php | 51 ------------------- app/code/Magento/Catalog/etc/di.xml | 2 +- .../Eav/Model/ResourceModel/CreateHandler.php | 16 ++++-- 3 files changed, 14 insertions(+), 55 deletions(-) delete mode 100644 app/code/Magento/Catalog/Model/Product/Option/CreateHandler.php diff --git a/app/code/Magento/Catalog/Model/Product/Option/CreateHandler.php b/app/code/Magento/Catalog/Model/Product/Option/CreateHandler.php deleted file mode 100644 index 3744124de034f..0000000000000 --- a/app/code/Magento/Catalog/Model/Product/Option/CreateHandler.php +++ /dev/null @@ -1,51 +0,0 @@ -optionRepository = $optionRepository; - } - - /** - * @param object $entity - * @param array $arguments - * @return \Magento\Catalog\Api\Data\ProductInterface|object - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function execute($entity, $arguments = []) - { - /** @var \Magento\Catalog\Api\Data\ProductInterface $entity */ - foreach ($this->optionRepository->getProductOptions($entity) as $option) { - $this->optionRepository->delete($option); - } - - if ($entity->getOptions()) { - foreach ($entity->getOptions() as $option) { - $this->optionRepository->save($option); - } - } - return $entity; - } -} diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index bb8cf5c1ac70e..49ad7d67d7058 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -606,7 +606,7 @@ Magento\Catalog\Model\Product\Website\ReadHandler - Magento\Catalog\Model\Product\Option\CreateHandler + Magento\Catalog\Model\Product\Option\SaveHandler Magento\Catalog\Model\Product\Gallery\CreateHandler Magento\Catalog\Model\Category\Link\SaveHandler Magento\Catalog\Model\Product\Website\SaveHandler diff --git a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php index f72bba39d6e29..8dd782a49218e 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php +++ b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php @@ -6,6 +6,7 @@ namespace Magento\Eav\Model\ResourceModel; use Magento\Eav\Api\AttributeRepositoryInterface as AttributeRepository; +use Magento\Eav\Model\Entity\AttributeCache; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\App\ObjectManager; use Magento\Framework\EntityManager\MetadataPool; @@ -43,6 +44,11 @@ class CreateHandler implements AttributeInterface */ private $scopeResolver; + /** + * @var AttributeCache + */ + private $attributeCache; + /** * @param AttributeRepository $attributeRepository * @param MetadataPool $metadataPool @@ -66,11 +72,15 @@ public function __construct( /** * @deprecated - * @return \Magento\Eav\Model\Entity\AttributeCache + * @return AttributeCache */ private function getAttributeCache() { - return ObjectManager::getInstance()->get(\Magento\Eav\Model\Entity\AttributeCache::class); + if ($this->attributeCache === null) { + $this->attributeCache = ObjectManager::getInstance()->get(AttributeCache::class); + } + + return $this->attributeCache; } /** @@ -80,7 +90,7 @@ private function getAttributeCache() */ protected function getAttributes($entityType) { - /** @var \Magento\Eav\Model\Entity\AttributeCache $cache */ + /** @var AttributeCache $cache */ $cache = $this->getAttributeCache(); if ($attributes = $cache->getAttributes($entityType)) { return $attributes; From 96682449ca8e358e12d8b49917deb4a4f401ab6f Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Mon, 3 Oct 2016 16:24:17 +0300 Subject: [PATCH 16/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../CatalogInventory/Model/Stock/StockItemRepository.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php index 7858d3cc0cb08..679257ba443c0 100644 --- a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php +++ b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php @@ -154,8 +154,11 @@ public function save(\Magento\CatalogInventory\Api\Data\StockItemInterface $stoc { try { /** @var \Magento\Catalog\Model\Product $product */ - $product = $this->productFactory->create(); - $product->load($stockItem->getProductId()); + $product = $this->getProductCollectionFactory()->create() + ->setFlag('has_stock_status_filter') +                ->addIdFilter($stockItem->getProductId()) +                ->addFieldToSelect('type_id') +                ->getFirstItem(); if (!$product->getId()) { return $stockItem; @@ -192,6 +195,7 @@ public function save(\Magento\CatalogInventory\Api\Data\StockItemInterface $stoc return $stockItem; } + /** * @inheritdoc */ From 95b2b74ab19501e545789936c3a4c344b7b8706d Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Mon, 3 Oct 2016 16:30:01 +0300 Subject: [PATCH 17/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../CatalogInventory/Model/Stock/StockItemRepository.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php index 679257ba443c0..e18db58710a20 100644 --- a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php +++ b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php @@ -156,9 +156,9 @@ public function save(\Magento\CatalogInventory\Api\Data\StockItemInterface $stoc /** @var \Magento\Catalog\Model\Product $product */ $product = $this->getProductCollectionFactory()->create() ->setFlag('has_stock_status_filter') -                ->addIdFilter($stockItem->getProductId()) -                ->addFieldToSelect('type_id') -                ->getFirstItem(); + ->addIdFilter($stockItem->getProductId()) + ->addFieldToSelect('type_id') + ->getFirstItem(); if (!$product->getId()) { return $stockItem; From 0474352e3f0bffbf1b4e4e2526a54702c819fb96 Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Tue, 4 Oct 2016 09:56:31 +0300 Subject: [PATCH 18/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../Magento/CatalogInventory/Model/Stock/StockItemRepository.php | 1 - .../Test/Unit/Model/Stock/StockItemRepositoryTest.php | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php index e18db58710a20..19d5f8e90432b 100644 --- a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php +++ b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php @@ -195,7 +195,6 @@ public function save(\Magento\CatalogInventory\Api\Data\StockItemInterface $stoc return $stockItem; } - /** * @inheritdoc */ diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php index d7efcf11e8057..02b7b34b35a9e 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/StockItemRepositoryTest.php @@ -188,6 +188,7 @@ protected function setUp() \Magento\Catalog\Model\ResourceModel\Product\Collection::class )->disableOriginalConstructor()->getMock(); + $productCollection->expects($this->any())->method('setFlag')->willReturnSelf(); $productCollection->expects($this->any())->method('addIdFilter')->willReturnSelf(); $productCollection->expects($this->any())->method('addFieldToSelect')->willReturnSelf(); $productCollection->expects($this->any())->method('getFirstItem')->willReturn($this->productMock); From ac6f2093d59317a49d755e25ca117994207f2d2c Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Tue, 4 Oct 2016 11:10:38 +0300 Subject: [PATCH 19/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../Model/Stock/StockItemRepository.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php index 947e4206a35e5..19d5f8e90432b 100644 --- a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php +++ b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php @@ -90,6 +90,9 @@ class StockItemRepository implements StockItemRepositoryInterface */ protected $stockRegistryStorage; + /** @var \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory */ + protected $productCollectionFactory; + /** * @param StockConfigurationInterface $stockConfiguration * @param StockStateProviderInterface $stockStateProvider @@ -130,6 +133,20 @@ public function __construct( $this->dateTime = $dateTime; } + /** + * @return \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory + */ + private function getProductCollectionFactory() + { + if ($this->productCollectionFactory === null) { + $this->productCollectionFactory = ObjectManager::getInstance()->get( + \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory::class + ); + } + + return $this->productCollectionFactory; + } + /** * @inheritdoc */ From 8b30062aa5aa08621bb857d9aca5b848f42ec553 Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Wed, 5 Oct 2016 13:18:55 +0300 Subject: [PATCH 20/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../Eav/Model/ResourceModel/CreateHandler.php | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php index 8dd782a49218e..16396e51a7135 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php +++ b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php @@ -19,6 +19,9 @@ */ class CreateHandler implements AttributeInterface { + /** Name of ATTRIBUTE_SET_ID field */ + const ATTRIBUTE_SET_ID = 'attribute_set_id'; + /** * @var AttributeRepository */ @@ -88,21 +91,35 @@ private function getAttributeCache() * @return \Magento\Eav\Api\Data\AttributeInterface[] * @throws \Exception */ - protected function getAttributes($entityType) + protected function getAttributes($entityType, $attributeSetId = null) { /** @var AttributeCache $cache */ $cache = $this->getAttributeCache(); - if ($attributes = $cache->getAttributes($entityType)) { + $suffix = 'attribute_set_id-' . ($attributeSetId ?: 'all'); + if ($attributes = $cache->getAttributes($entityType, $suffix)) { return $attributes; } $metadata = $this->metadataPool->getMetadata($entityType); + if ($attributeSetId === null) { + $criteria = $this->searchCriteriaBuilder->addFilter(self::ATTRIBUTE_SET_ID . '', null, 'neq')->create(); + } else { + $criteria = $this->searchCriteriaBuilder->addFilter(self::ATTRIBUTE_SET_ID, $attributeSetId)->create(); + } + $searchResult = $this->attributeRepository->getList( $metadata->getEavEntityType(), - $this->searchCriteriaBuilder->addFilter('attribute_set_id', null, 'neq')->create() + $criteria + ); + $attributes = $searchResult->getItems(); + + $this->attributeCache->saveAttributes( + $entityType, + $attributes, + $suffix ); - return $searchResult->getItems(); + return $attributes; } /** @@ -120,8 +137,9 @@ public function execute($entityType, $entityData, $arguments = []) if ($metadata->getEavEntityType()) { $processed = []; $entityLinkField = $metadata->getLinkField(); + $attributeSetId = isset($entityData[self::ATTRIBUTE_SET_ID]) ? $entityData[self::ATTRIBUTE_SET_ID] : null; /** @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute */ - foreach ($this->getAttributes($entityType) as $attribute) { + foreach ($this->getAttributes($entityType, $attributeSetId) as $attribute) { if ($attribute->isStatic()) { continue; } From b7477571f41f7b1973155eda16f9ef59042df534 Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Thu, 6 Oct 2016 22:24:24 +0300 Subject: [PATCH 21/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../Catalog/Model/ResourceModel/Category.php | 4 +- .../Model/ResourceModel/AttributeLoader.php | 95 +++++++++++++++++++ .../Eav/Model/ResourceModel/CreateHandler.php | 61 +++--------- .../Eav/Model/ResourceModel/UpdateHandler.php | 33 +++---- ...h_shipping_method_and_items_categories.php | 2 +- 5 files changed, 126 insertions(+), 69 deletions(-) create mode 100644 app/code/Magento/Eav/Model/ResourceModel/AttributeLoader.php diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Category.php b/app/code/Magento/Catalog/Model/ResourceModel/Category.php index 407c2027923de..e743c5d384bdd 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Category.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Category.php @@ -238,7 +238,9 @@ protected function _beforeSave(\Magento\Framework\DataObject $object) if (!$object->getChildrenCount()) { $object->setChildrenCount(0); } - + $object->setAttributeSetId( + $object->getAttributeSetId() ?: $this->getEntityType()->getDefaultAttributeSetId() + ); if ($object->isObjectNew()) { if ($object->getPosition() === null) { $object->setPosition($this->_getMaxPosition($object->getPath()) + 1); diff --git a/app/code/Magento/Eav/Model/ResourceModel/AttributeLoader.php b/app/code/Magento/Eav/Model/ResourceModel/AttributeLoader.php new file mode 100644 index 0000000000000..e2c2b7d0275a2 --- /dev/null +++ b/app/code/Magento/Eav/Model/ResourceModel/AttributeLoader.php @@ -0,0 +1,95 @@ +attributeRepository = $attributeRepository; + $this->metadataPool = $metadataPool; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + $this->attributeCache = $attributeCache; + } + + /** + * Get attributes list from attribute set + * + * @param string $entityType + * @param int $attributeSetId + * @return \Magento\Eav\Api\Data\AttributeInterface[]|\object[] + */ + public function getAttributes($entityType, $attributeSetId = null) + { + $suffix = self::ATTRIBUTE_SET_ID . '-' . ($attributeSetId ?: 'all'); + if ($attributes = $this->attributeCache->getAttributes($entityType, $suffix)) { + return $attributes; + } + + $metadata = $this->metadataPool->getMetadata($entityType); + + if ($attributeSetId === null) { + $criteria = $this->searchCriteriaBuilder->addFilter(self::ATTRIBUTE_SET_ID, null, 'neq')->create(); + } else { + $criteria = $this->searchCriteriaBuilder->addFilter(self::ATTRIBUTE_SET_ID, $attributeSetId)->create(); + } + + $searchResult = $this->attributeRepository->getList( + $metadata->getEavEntityType(), + $criteria + ); + $attributes = $searchResult->getItems(); + + $this->attributeCache->saveAttributes( + $entityType, + $attributes, + $suffix + ); + return $attributes; + } +} diff --git a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php index 16396e51a7135..764a0208f2129 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php +++ b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php @@ -6,7 +6,6 @@ namespace Magento\Eav\Model\ResourceModel; use Magento\Eav\Api\AttributeRepositoryInterface as AttributeRepository; -use Magento\Eav\Model\Entity\AttributeCache; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\App\ObjectManager; use Magento\Framework\EntityManager\MetadataPool; @@ -19,9 +18,6 @@ */ class CreateHandler implements AttributeInterface { - /** Name of ATTRIBUTE_SET_ID field */ - const ATTRIBUTE_SET_ID = 'attribute_set_id'; - /** * @var AttributeRepository */ @@ -48,9 +44,9 @@ class CreateHandler implements AttributeInterface private $scopeResolver; /** - * @var AttributeCache + * @var AttributeLoader */ - private $attributeCache; + private $attributeLoader; /** * @param AttributeRepository $attributeRepository @@ -58,68 +54,32 @@ class CreateHandler implements AttributeInterface * @param SearchCriteriaBuilder $searchCriteriaBuilder * @param AttributePersistor $attributePersistor * @param ScopeResolver $scopeResolver + * @param AttributeLoader $attributeLoader */ public function __construct( AttributeRepository $attributeRepository, MetadataPool $metadataPool, SearchCriteriaBuilder $searchCriteriaBuilder, AttributePersistor $attributePersistor, - ScopeResolver $scopeResolver + ScopeResolver $scopeResolver, + AttributeLoader $attributeLoader = null ) { $this->attributeRepository = $attributeRepository; $this->metadataPool = $metadataPool; $this->searchCriteriaBuilder = $searchCriteriaBuilder; $this->attributePersistor = $attributePersistor; $this->scopeResolver = $scopeResolver; - } - - /** - * @deprecated - * @return AttributeCache - */ - private function getAttributeCache() - { - if ($this->attributeCache === null) { - $this->attributeCache = ObjectManager::getInstance()->get(AttributeCache::class); - } - - return $this->attributeCache; + $this->attributeLoader = $attributeLoader ?: ObjectManager::getInstance()->get(AttributeLoader::class); } /** * @param string $entityType + * @param int $attributeSetId * @return \Magento\Eav\Api\Data\AttributeInterface[] - * @throws \Exception */ protected function getAttributes($entityType, $attributeSetId = null) { - /** @var AttributeCache $cache */ - $cache = $this->getAttributeCache(); - $suffix = 'attribute_set_id-' . ($attributeSetId ?: 'all'); - if ($attributes = $cache->getAttributes($entityType, $suffix)) { - return $attributes; - } - - $metadata = $this->metadataPool->getMetadata($entityType); - - if ($attributeSetId === null) { - $criteria = $this->searchCriteriaBuilder->addFilter(self::ATTRIBUTE_SET_ID . '', null, 'neq')->create(); - } else { - $criteria = $this->searchCriteriaBuilder->addFilter(self::ATTRIBUTE_SET_ID, $attributeSetId)->create(); - } - - $searchResult = $this->attributeRepository->getList( - $metadata->getEavEntityType(), - $criteria - ); - $attributes = $searchResult->getItems(); - - $this->attributeCache->saveAttributes( - $entityType, - $attributes, - $suffix - ); - return $attributes; + return $this->attributeLoader->getAttributes($entityType, $attributeSetId); } /** @@ -137,9 +97,12 @@ public function execute($entityType, $entityData, $arguments = []) if ($metadata->getEavEntityType()) { $processed = []; $entityLinkField = $metadata->getLinkField(); - $attributeSetId = isset($entityData[self::ATTRIBUTE_SET_ID]) ? $entityData[self::ATTRIBUTE_SET_ID] : null; + $attributeSetId = isset($entityData[AttributeLoader::ATTRIBUTE_SET_ID]) + ? $entityData[AttributeLoader::ATTRIBUTE_SET_ID] + : null; // @todo verify is it normal to not have attributer_set_id /** @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute */ foreach ($this->getAttributes($entityType, $attributeSetId) as $attribute) { + $ac[] = $attribute->getAttributeCode(); if ($attribute->isStatic()) { continue; } diff --git a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php index c917a82b6701f..8181f6a136112 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php +++ b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php @@ -54,6 +54,11 @@ class UpdateHandler implements AttributeInterface */ private $readHandler; + /** + * @var AttributeLoader + */ + private $attributeLoader; + /** * UpdateHandler constructor. * @param AttributeRepository $attributeRepository @@ -69,7 +74,8 @@ public function __construct( SearchCriteriaBuilder $searchCriteriaBuilder, AttributePersistor $attributePersistor, ReadSnapshot $readSnapshot, - ScopeResolver $scopeResolver + ScopeResolver $scopeResolver, + AttributeLoader $attributeLoader = null ) { $this->attributeRepository = $attributeRepository; $this->metadataPool = $metadataPool; @@ -77,6 +83,7 @@ public function __construct( $this->attributePersistor = $attributePersistor; $this->readSnapshot = $readSnapshot; $this->scopeResolver = $scopeResolver; + $this->attributeLoader = $attributeLoader ?: ObjectManager::getInstance()->get(AttributeLoader::class); } /** @@ -90,26 +97,13 @@ private function getAttributeCache() /** * @param string $entityType + * @param int $attributeSetId * @return \Magento\Eav\Api\Data\AttributeInterface[] - * @throws \Exception */ - protected function getAttributes($entityType) + protected function getAttributes($entityType, $attributeSetId = null) { - /** @var \Magento\Eav\Model\Entity\AttributeCache $cache */ - $cache = $this->getAttributeCache(); - if ($attributes = $cache->getAttributes($entityType)) { - return $attributes; - } - - $metadata = $this->metadataPool->getMetadata($entityType); - - $searchResult = $this->attributeRepository->getList( - $metadata->getEavEntityType(), - $this->searchCriteriaBuilder->addFilter('attribute_set_id', null, 'neq')->create() - ); - return $searchResult->getItems(); + return $this->attributeLoader->getAttributes($entityType, $attributeSetId); } - /** * @param string $entityType * @param array $entityData @@ -133,8 +127,11 @@ public function execute($entityType, $entityData, $arguments = []) $entityDataForSnapshot[$scope->getIdentifier()] = $entityData[$scope->getIdentifier()]; } } + $attributeSetId = isset($entityData[AttributeLoader::ATTRIBUTE_SET_ID]) + ? $entityData[AttributeLoader::ATTRIBUTE_SET_ID] + : null; // @todo verify is it normal to not have attributer_set_id $snapshot = $this->readSnapshot->execute($entityType, $entityDataForSnapshot); - foreach ($this->getAttributes($entityType) as $attribute) { + foreach ($this->getAttributes($entityType, $attributeSetId) as $attribute) { if ($attribute->isStatic()) { continue; } diff --git a/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_shipping_method_and_items_categories.php b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_shipping_method_and_items_categories.php index 7fb35a4412bf8..ec230f3c51261 100644 --- a/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_shipping_method_and_items_categories.php +++ b/dev/tests/integration/testsuite/Magento/Checkout/_files/quote_with_shipping_method_and_items_categories.php @@ -56,7 +56,7 @@ )->setId( 444 )->setAttributeSetId( - 5 + 4 )->setStoreId( 1 )->setWebsiteIds( From 9125a91fd8bd2b77d097270bf6258bfe613fb868 Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Fri, 7 Oct 2016 13:18:02 +0300 Subject: [PATCH 22/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../Magento/Eav/Model/ResourceModel/CreateHandler.php | 1 - .../Magento/Eav/Model/ResourceModel/UpdateHandler.php | 11 ++--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php index 764a0208f2129..df411b6a21698 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php +++ b/app/code/Magento/Eav/Model/ResourceModel/CreateHandler.php @@ -102,7 +102,6 @@ public function execute($entityType, $entityData, $arguments = []) : null; // @todo verify is it normal to not have attributer_set_id /** @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute */ foreach ($this->getAttributes($entityType, $attributeSetId) as $attribute) { - $ac[] = $attribute->getAttributeCode(); if ($attribute->isStatic()) { continue; } diff --git a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php index 8181f6a136112..c775a24a03c46 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php +++ b/app/code/Magento/Eav/Model/ResourceModel/UpdateHandler.php @@ -67,6 +67,7 @@ class UpdateHandler implements AttributeInterface * @param AttributePersistor $attributePersistor * @param ReadSnapshot $readSnapshot * @param ScopeResolver $scopeResolver + * @param AttributeLoader $attributeLoader */ public function __construct( AttributeRepository $attributeRepository, @@ -86,15 +87,6 @@ public function __construct( $this->attributeLoader = $attributeLoader ?: ObjectManager::getInstance()->get(AttributeLoader::class); } - /** - * @deprecated - * @return \Magento\Eav\Model\Entity\AttributeCache - */ - private function getAttributeCache() - { - return ObjectManager::getInstance()->get(\Magento\Eav\Model\Entity\AttributeCache::class); - } - /** * @param string $entityType * @param int $attributeSetId @@ -104,6 +96,7 @@ protected function getAttributes($entityType, $attributeSetId = null) { return $this->attributeLoader->getAttributes($entityType, $attributeSetId); } + /** * @param string $entityType * @param array $entityData From 3798bc9b049554392250a9666c73a8f642f9b84d Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Mon, 10 Oct 2016 15:40:24 +0300 Subject: [PATCH 23/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../ConfigurableProduct/Model/Product/VariationHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php b/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php index 733e382bccf46..0d0bba60a7777 100644 --- a/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php +++ b/app/code/Magento/ConfigurableProduct/Model/Product/VariationHandler.php @@ -73,6 +73,7 @@ public function __construct( public function generateSimpleProducts($parentProduct, $productsData) { $generatedProductIds = []; + $this->attributes = null; $productsData = $this->duplicateImagesForVariations($productsData); foreach ($productsData as $simpleProductData) { $newSimpleProduct = $this->productFactory->create(); @@ -92,7 +93,6 @@ public function generateSimpleProducts($parentProduct, $productsData) $generatedProductIds[] = $newSimpleProduct->getId(); } - $this->attributes = null; return $generatedProductIds; } From d9dd253fa3535fa788e86cad99c460a7ee424587 Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Mon, 10 Oct 2016 21:32:48 +0300 Subject: [PATCH 24/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../Magento/TestFramework/Annotation/ApiDataFixture.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiDataFixture.php b/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiDataFixture.php index 5eeb7ddb61fa0..0ad53eeeb90d9 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiDataFixture.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/Annotation/ApiDataFixture.php @@ -51,6 +51,7 @@ public function __construct($fixtureBaseDir) */ public function startTest(\PHPUnit_Framework_TestCase $test) { + \Magento\TestFramework\Helper\Bootstrap::getInstance()->reinitialize(); /** Apply method level fixtures if thy are available, apply class level fixtures otherwise */ $this->_applyFixtures($this->_getFixtures('method', $test) ?: $this->_getFixtures('class', $test)); } @@ -61,6 +62,9 @@ public function startTest(\PHPUnit_Framework_TestCase $test) public function endTest() { $this->_revertFixtures(); + /** @var $objectManager \Magento\TestFramework\ObjectManager */ + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $objectManager->get(\Magento\Eav\Model\Entity\AttributeCache::class)->clear(); } /** From 066e7a5fb3762b78f33bc051e51b07edb85efcb9 Mon Sep 17 00:00:00 2001 From: Andrii Kasian Date: Wed, 12 Oct 2016 15:21:09 +0300 Subject: [PATCH 25/25] MAGETWO-55299: [Customer] Fast Save of Product Variations - MAGETWO-55787: Fast saving of product with high number of variations generated --- .../Magento/Catalog/Model/Product/Gallery/CreateHandler.php | 4 +++- .../Magento/Catalog/Model/ResourceModel/Product/Link.php | 6 +++--- .../CatalogInventory/Model/Stock/StockItemRepository.php | 1 + .../Magento/Eav/Model/ResourceModel/AttributeLoader.php | 4 +++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php index aaf6a5a8b79b0..cbf0464ca3661 100644 --- a/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php @@ -58,7 +58,9 @@ class CreateHandler implements ExtensionInterface */ protected $fileStorageDb; - /** @var array */ + /** + * @var array + */ private $mediaAttributeCodes; /** diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php index 2af9b51fde969..17bfa90e8842d 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php @@ -97,10 +97,10 @@ public function getProductLinkId($parentId, $linkedProductId, $typeId) } /** - * Is product has links + * Check if product has links. * - * @param int $parentId - * @return int + * @param int $parentId ID of product + * @return bool */ public function hasProductLinks($parentId) { diff --git a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php index 19d5f8e90432b..58e364920bb6a 100644 --- a/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php +++ b/app/code/Magento/CatalogInventory/Model/Stock/StockItemRepository.php @@ -134,6 +134,7 @@ public function __construct( } /** + * @deprecated * @return \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory */ private function getProductCollectionFactory() diff --git a/app/code/Magento/Eav/Model/ResourceModel/AttributeLoader.php b/app/code/Magento/Eav/Model/ResourceModel/AttributeLoader.php index e2c2b7d0275a2..439f550a2bf02 100644 --- a/app/code/Magento/Eav/Model/ResourceModel/AttributeLoader.php +++ b/app/code/Magento/Eav/Model/ResourceModel/AttributeLoader.php @@ -11,7 +11,9 @@ use Magento\Framework\EntityManager\MetadataPool; /** - * Class load and cache attributes from some attribute set + * Сlass responsible for loading and caching of attributes related to the given attribute set. + * + * Can be used to improve performance of services that mostly read attribute data. */ class AttributeLoader {