diff --git a/CHANGELOG.MD b/CHANGELOG.MD index f9edcd26..8b316b32 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -2,7 +2,12 @@ ## Unreleased -## [1.7.0] (2019.11.14) +## [1.8.0] (2019.11.20) + +### Added +- Add "final_price" to "configurable_children" ([#151](https://github.com/DivanteLtd/magento2-vsbridge-indexer/issues/151)) + +## [1.7.0] (2019.11.15) ### Changed/Improved - Speed up exporting for product/category data. Class `ConvertDataTypes` was removed, converting value to int/float is executed when loading product/category attributes. diff --git a/composer.json b/composer.json index e09ce43f..2308414b 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "name": "Agata", "email": "afirlejczyk@divante.pl" }], - "version": "1.7.0", + "version": "1.8.0", "keywords": [ "magento", "magento2", diff --git a/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/Configurable/ChildAttributesProcessor.php b/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/Configurable/ChildAttributesProcessor.php new file mode 100644 index 00000000..928a1419 --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/Configurable/ChildAttributesProcessor.php @@ -0,0 +1,162 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +declare(strict_types = 1); + +namespace Divante\VsbridgeIndexerCatalog\Model\Indexer\DataProvider\Product\Configurable; + +use Divante\VsbridgeIndexerCatalog\Model\Attributes\ConfigurableAttributes; +use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\AttributeDataProvider; +use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\Prices as PriceResourceModel; +use Divante\VsbridgeIndexerCatalog\Model\TierPriceProcessor; + +/** + * Class ChildAttributesProcessor + */ +class ChildAttributesProcessor +{ + /** + * @var int + */ + private $batchSize; + + /** + * @var TierPriceProcessor + */ + private $tierPriceProcessor; + + /** + * @var PriceResourceModel + */ + private $priceResourceModel; + + /** + * @var AttributeDataProvider + */ + private $resourceAttributeModel; + + /** + * @var ConfigurableAttributes + */ + private $configurableAttributes; + + /** + * ChildAttributesProcessor constructor. + * + * @param AttributeDataProvider $attributeDataProvider + * @param ConfigurableAttributes $configurableAttributes + * @param TierPriceProcessor $tierPriceProcessor + * @param PriceResourceModel $priceResourceModel + * @param int $batchSize + */ + public function __construct( + AttributeDataProvider $attributeDataProvider, + ConfigurableAttributes $configurableAttributes, + TierPriceProcessor $tierPriceProcessor, + PriceResourceModel $priceResourceModel, + $batchSize = 500 + ) { + $this->batchSize = $batchSize; + $this->tierPriceProcessor = $tierPriceProcessor; + $this->priceResourceModel = $priceResourceModel; + $this->resourceAttributeModel = $attributeDataProvider; + $this->configurableAttributes = $configurableAttributes; + } + + /** + * @param int $storeId + * @param array $allChildren + * @param array $configurableAttributeCodes + * + * @return array + * @throws \Exception + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function loadChildrenRawAttributesInBatches($storeId, array $allChildren, array $configurableAttributeCodes) + { + $requiredAttributes = array_merge( + $this->getRequiredChildrenAttributes(), + $configurableAttributeCodes + ); + + $requiredAttribute = array_unique($requiredAttributes); + + foreach ($this->getChildrenInBatches($allChildren, $this->batchSize) as $batch) { + $childIds = array_keys($batch); + $priceData = $this->priceResourceModel->loadPriceData($storeId, $childIds); + $allAttributesData = $this->resourceAttributeModel->loadAttributesData( + $storeId, + $childIds, + $requiredAttribute + ); + + foreach ($priceData as $childId => $priceDataRow) { + $allChildren[$childId]['final_price'] = (float)$priceDataRow['final_price']; + $allChildren[$childId]['regular_price'] = (float)$priceDataRow['price']; + } + + foreach ($allAttributesData as $childId => $attributes) { + if ($this->tierPriceProcessor->syncTierPrices()) { + /*we need some extra attributes to apply tier prices*/ + $batch[$childId] = array_merge( + $allChildren[$childId], + $attributes + ); + } else { + $allChildren[$childId] = array_merge( + $allChildren[$childId], + $attributes + ); + } + } + + if ($this->tierPriceProcessor->syncTierPrices()) { + $batch = $this->tierPriceProcessor->applyTierGroupPrices($batch, $storeId); + $allChildren = array_replace_recursive($allChildren, $batch); + } + + $batch = null; + } + + return $allChildren; + } + + /** + * @return array + */ + private function getRequiredChildrenAttributes(): array + { + return $this->configurableAttributes->getChildrenRequiredAttributes(); + } + + /** + * @param array $documents + * @param int $batchSize + * + * @return \Generator + */ + private function getChildrenInBatches(array $documents, $batchSize) + { + $i = 0; + $batch = []; + + foreach ($documents as $documentName => $documentValue) { + $batch[$documentName] = $documentValue; + + if (++$i == $batchSize) { + yield $batch; + $i = 0; + $batch = []; + } + } + + if (count($batch) > 0) { + yield $batch; + } + } +} diff --git a/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/ConfigurableData.php b/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/ConfigurableData.php index ca9cd5fb..070a76d4 100644 --- a/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/ConfigurableData.php +++ b/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/ConfigurableData.php @@ -8,13 +8,11 @@ namespace Divante\VsbridgeIndexerCatalog\Model\Indexer\DataProvider\Product; +use Divante\VsbridgeIndexerCatalog\Api\LoadInventoryInterface; use Divante\VsbridgeIndexerCatalog\Model\ConfigurableProcessor\GetConfigurableOptions; -use Divante\VsbridgeIndexerCatalog\Model\Attributes\ConfigurableAttributes; +use Divante\VsbridgeIndexerCatalog\Model\Indexer\DataProvider\Product\Configurable\ChildAttributesProcessor; use Divante\VsbridgeIndexerCatalog\Model\InventoryProcessor; -use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\AttributeDataProvider; use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\Configurable as ConfigurableResource; -use Divante\VsbridgeIndexerCatalog\Api\LoadInventoryInterface; -use Divante\VsbridgeIndexerCatalog\Model\TierPriceProcessor; use Divante\VsbridgeIndexerCore\Api\DataProviderInterface; use Divante\VsbridgeIndexerCore\Indexer\DataFilter; use Magento\ConfigurableProduct\Model\Product\Type\Configurable as ConfigurableType; @@ -24,12 +22,6 @@ */ class ConfigurableData implements DataProviderInterface { - - /** - * @var int - */ - private $batchSize = 500; - /** * @var array */ @@ -51,11 +43,6 @@ class ConfigurableData implements DataProviderInterface */ private $configurableResource; - /** - * @var AttributeDataProvider - */ - private $resourceAttributeModel; - /** * @var LoadInventoryInterface */ @@ -67,14 +54,9 @@ class ConfigurableData implements DataProviderInterface private $inventoryProcessor; /** - * @var ConfigurableAttributes - */ - private $configurableAttributes; - - /** - * @var TierPriceProcessor + * @var ChildAttributesProcessor */ - private $tierPriceProcessor; + private $childrenAttributeProcessor; /** * @var GetConfigurableOptions @@ -86,31 +68,25 @@ class ConfigurableData implements DataProviderInterface * * @param DataFilter $dataFilter * @param ConfigurableResource $configurableResource - * @param AttributeDataProvider $attributeResource * @param LoadInventoryInterface $loadInventory * @param GetConfigurableOptions $configurableProcessor - * @param ConfigurableAttributes $configurableAttributes - * @param TierPriceProcessor $tierPriceProcessor + * @param ChildAttributesProcessor $childrenAttributeProcessor * @param InventoryProcessor $inventoryProcessor */ public function __construct( DataFilter $dataFilter, ConfigurableResource $configurableResource, - AttributeDataProvider $attributeResource, LoadInventoryInterface $loadInventory, GetConfigurableOptions $configurableProcessor, - ConfigurableAttributes $configurableAttributes, - TierPriceProcessor $tierPriceProcessor, + ChildAttributesProcessor $childrenAttributeProcessor, InventoryProcessor $inventoryProcessor ) { $this->dataFilter = $dataFilter; $this->configurableResource = $configurableResource; - $this->resourceAttributeModel = $attributeResource; $this->loadInventory = $loadInventory; $this->inventoryProcessor = $inventoryProcessor; - $this->tierPriceProcessor = $tierPriceProcessor; + $this->childrenAttributeProcessor = $childrenAttributeProcessor; $this->configurableProcessor = $configurableProcessor; - $this->configurableAttributes = $configurableAttributes; } /** @@ -167,21 +143,15 @@ private function prepareConfigurableChildrenAttributes(array $indexData, $storeI $stockRowData = $this->loadInventory->execute($allChildren, $storeId); $configurableAttributeCodes = $this->configurableResource->getConfigurableAttributeCodes(); - $requiredAttributes = array_merge( - $this->getRequiredChildrenAttributes(), - $configurableAttributeCodes - ); - - $requiredAttribute = array_unique($requiredAttributes); - $allChildren = $this->loadChildrenRawAttributesInBatches($storeId, $allChildren, $requiredAttribute); + $allChildren = $this->childrenAttributeProcessor + ->loadChildrenRawAttributesInBatches($storeId, $allChildren, $configurableAttributeCodes); foreach ($allChildren as $child) { $childId = $child['entity_id']; $child['id'] = (int) $childId; $parentIds = $child['parent_ids']; - // @TODO add support for final_price in configurable_children -> check if it really necessary. Probably not - if (isset($child['price'])) { + if (!isset($child['regular_price']) && isset($child['price'])) { $child['regular_price'] = $child['price']; } @@ -209,14 +179,6 @@ private function prepareConfigurableChildrenAttributes(array $indexData, $storeI return $indexData; } - /** - * @return array - */ - private function getRequiredChildrenAttributes() - { - return $this->configurableAttributes->getChildrenRequiredAttributes(); - } - /** * Apply attributes to product variants + extra options for products necessary for vsf * @param array $productDTO @@ -278,7 +240,7 @@ private function prepareConfigurableProduct(array $productDTO) { $configurableChildren = $productDTO['configurable_children']; $areChildInStock = 0; - $childPrice = []; + $finalPrice = $childPrice = []; $hasPrice = $this->hasPrice($productDTO); foreach ($configurableChildren as $child) { @@ -287,12 +249,13 @@ private function prepareConfigurableProduct(array $productDTO) } $childPrice[] = $child['price']; + $finalPrice[] = $child['final_price'] ?? $child['final_price'] ?? $child['price']; } if (!$hasPrice && !empty($childPrice)) { $minPrice = min($childPrice); $productDTO['price'] = $minPrice; - $productDTO['final_price'] = $minPrice; + $productDTO['final_price'] = min($finalPrice); $productDTO['regular_price'] = $minPrice; } @@ -331,77 +294,6 @@ private function hasPrice(array $product) return true; } - /** - * @param int $storeId - * @param array $allChildren - * @param array $requiredAttributes - * - * @return array - * @throws \Exception - * @throws \Magento\Framework\Exception\LocalizedException - */ - private function loadChildrenRawAttributesInBatches($storeId, array $allChildren, array $requiredAttributes) - { - $requiredAttribute = array_unique($requiredAttributes); - - foreach ($this->getChildrenInBatches($allChildren, $this->batchSize) as $batch) { - $childIds = array_keys($batch); - $allAttributesData = $this->resourceAttributeModel->loadAttributesData( - $storeId, - $childIds, - $requiredAttribute - ); - - foreach ($allAttributesData as $productId => $attributes) { - if ($this->tierPriceProcessor->syncTierPrices()) { - /*we need some extra attributes to apply tier prices*/ - $batch[$productId] = array_merge( - $allChildren[$productId], - $attributes - ); - } else { - $allChildren[$productId] = array_merge( - $allChildren[$productId], - $attributes - ); - } - } - - if ($this->tierPriceProcessor->syncTierPrices()) { - $batch = $this->tierPriceProcessor->applyTierGroupPrices($batch, $storeId); - $allChildren = array_replace_recursive($allChildren, $batch); - } - } - - return $allChildren; - } - - /** - * @param array $documents - * @param int $batchSize - * - * @return \Generator - */ - private function getChildrenInBatches(array $documents, $batchSize) - { - $i = 0; - $batch = []; - - foreach ($documents as $documentName => $documentValue) { - $batch[$documentName] = $documentValue; - - if (++$i == $batchSize) { - yield $batch; - $i = 0; - $batch = []; - } - } - - if (count($batch) > 0) { - yield $batch; - } - } - /** * @param array $productData * diff --git a/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/PriceData.php b/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/PriceData.php index 7a5d0034..7e761daf 100644 --- a/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/PriceData.php +++ b/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/PriceData.php @@ -55,8 +55,7 @@ public function addData(array $indexData, $storeId) $productIds = array_keys($indexData); $priceData = $this->resourcePriceModel->loadPriceData($storeId, $productIds); - foreach ($priceData as $priceDataRow) { - $productId = $priceDataRow['entity_id']; + foreach ($priceData as $productId => $priceDataRow) { $indexData[$productId]['final_price'] = $this->preparePrice($priceDataRow['final_price']); $indexData[$productId]['regular_price'] = $this->preparePrice($priceDataRow['price']); } diff --git a/src/module-vsbridge-indexer-catalog/Model/Product/PriceTableResolverProxy.php b/src/module-vsbridge-indexer-catalog/Model/Product/PriceTableResolverProxy.php index de99ae46..570481e9 100644 --- a/src/module-vsbridge-indexer-catalog/Model/Product/PriceTableResolverProxy.php +++ b/src/module-vsbridge-indexer-catalog/Model/Product/PriceTableResolverProxy.php @@ -6,6 +6,8 @@ * @license See LICENSE_DIVANTE.txt for license details. */ +declare(strict_types = 1); + namespace Divante\VsbridgeIndexerCatalog\Model\Product; use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; @@ -58,9 +60,9 @@ public function __construct(ObjectManagerInterface $objectManager) * @param int $websiteId * @param int $customerGroupId * - * @return mixed|string + * @return string */ - public function resolve(int $websiteId, int $customerGroupId) + public function resolve(int $websiteId, int $customerGroupId): string { if (class_exists('\Magento\Catalog\Model\Indexer\Product\Price\PriceTableResolver')) { $this->createDimensionFactory(); diff --git a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Prices.php b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Prices.php index 9b2e572f..b724a051 100644 --- a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Prices.php +++ b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Prices.php @@ -61,7 +61,8 @@ public function __construct( } /** - * @param int $storeId + * Only default customer Group ID (0) is supported now + * @param int $storeId * @param array $productIds * * @return array @@ -72,7 +73,7 @@ public function loadPriceData(int $storeId, array $productIds): array $entityIdField = $this->productMetaData->get()->getIdentifierField(); $websiteId = (int)$this->getStore($storeId)->getWebsiteId(); - // only default customer group Id is supported now + // Only default customer Group ID (0) is supported now $customerGroupId = 0; $priceIndexTableName = $this->getPriceIndexTableName($websiteId, $customerGroupId); @@ -89,7 +90,7 @@ public function loadPriceData(int $storeId, array $productIds): array ->where('p.website_id = ?', $websiteId) ->where("p.$entityIdField IN (?)", $productIds); - return $this->getConnection()->fetchAll($select); + return $this->getConnection()->fetchAssoc($select); } /**