Skip to content

Commit

Permalink
Merge pull request #496 from magento-performance/MAGETWO-56986
Browse files Browse the repository at this point in the history
[Performance] MAGETWO-55299: Fast Save of Product Variations
  • Loading branch information
kandy authored Oct 12, 2016
2 parents 073f227 + 7981447 commit bc9df94
Show file tree
Hide file tree
Showing 20 changed files with 327 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
24 changes: 22 additions & 2 deletions app/code/Magento/Catalog/Model/Product/Gallery/CreateHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ class CreateHandler implements ExtensionInterface
*/
protected $fileStorageDb;

/**
* @var array
*/
private $mediaAttributeCodes;

/**
* @param \Magento\Framework\EntityManager\MetadataPool $metadataPool
* @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository
Expand Down Expand Up @@ -145,9 +150,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');
}
Expand Down Expand Up @@ -394,4 +401,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;
}
}
17 changes: 11 additions & 6 deletions app/code/Magento/Catalog/Model/Product/Link/SaveHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ public function __construct(
Link $linkResource,
ProductLinkRepositoryInterface $productLinkRepository
) {

$this->metadataPool = $metadataPool;
$this->linkResource = $linkResource;
$this->productLinkRepository = $productLinkRepository;
Expand All @@ -54,12 +53,18 @@ 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);
$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);
}
}
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;
}
Expand Down
4 changes: 3 additions & 1 deletion app/code/Magento/Catalog/Model/ResourceModel/Category.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
11 changes: 4 additions & 7 deletions app/code/Magento/Catalog/Model/ResourceModel/Eav/Attribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down
24 changes: 24 additions & 0 deletions app/code/Magento/Catalog/Model/ResourceModel/Product/Link.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,30 @@ public function getProductLinkId($parentId, $linkedProductId, $typeId)
return $connection->fetchOne($select, $bind);
}

/**
* Check if product has links.
*
* @param int $parentId ID of product
* @return bool
*/
public function hasProductLinks($parentId)
{
$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' => $parentId
]
) > 0;
}

/**
* Save Product Links process
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
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;
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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -129,15 +133,34 @@ public function __construct(
$this->dateTime = $dateTime;
}

/**
* @deprecated
* @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
*/
public function save(\Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem)
{
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
*/
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;
Expand Down Expand Up @@ -153,9 +155,8 @@ protected function setUp()
->disableOriginalConstructor()
->setMethods(['load', 'getId', 'getTypeId', '__wakeup'])
->getMock();
$this->productFactoryMock->expects($this->any())
->method('create')
->willReturn($this->productMock);

$this->productFactoryMock->expects($this->any())->method('create')->willReturn($this->productMock);

$this->queryBuilderFactoryMock = $this->getMockBuilder(\Magento\Framework\DB\QueryBuilderFactory::class)
->setMethods(['create'])
Expand Down Expand Up @@ -185,6 +186,22 @@ protected function setUp()
->disableOriginalConstructor()
->getMock();

$productCollection = $this->getMockBuilder(
\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);

$productCollectionFactory = $this->getMockBuilder(CollectionFactory::class)
->setMethods(['create'])
->disableOriginalConstructor()
->getMock();

$productCollectionFactory->expects($this->any())->method('create')->willReturn($productCollection);

$this->model = (new ObjectManager($this))->getObject(
StockItemRepository::class,
[
Expand All @@ -200,6 +217,7 @@ protected function setUp()
'indexProcessor' => $this->indexProcessorMock,
'dateTime' => $this->dateTime,
'stockRegistryStorage' => $this->stockRegistryStorage,
'productCollectionFactory' => $productCollectionFactory,
]
);
}
Expand Down Expand Up @@ -263,7 +281,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);
Expand Down Expand Up @@ -309,7 +326,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');
Expand All @@ -325,7 +341,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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class UpdateConfigurations
'swatch_image',
'small_image',
'thumbnail',
'image'
'image',
];

/**
Expand Down Expand Up @@ -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;
Expand All @@ -91,6 +93,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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -70,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();
Expand Down Expand Up @@ -160,7 +164,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' ||
Expand Down
Loading

0 comments on commit bc9df94

Please sign in to comment.