diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 67bf0418..5f914947 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -2,7 +2,15 @@ ## Unreleased +### Fixes +- Fix `vsbridge:reindex` command, fix initialize right areacode, loading di.xml files for adminhtml areacode. ([#127](https://github.com/DivanteLtd/magento2-vsbridge-indexer/pull/127)) +- Fix `vsbridge:reindex --all` command, data will be reindex store by store. ([#130](https://github.com/DivanteLtd/magento2-vsbridge-indexer/pull/130)) +### Changed/Improved +- Rework abstract mapper to allow configure static type map in di.xml ([#125](https://github.com/DivanteLtd/magento2-vsbridge-indexer/pull/125)) +- Minor refactoring for product resource model ([#126](https://github.com/DivanteLtd/magento2-vsbridge-indexer/pull/126/)). +- Vsbridge Category Indexer - Save Mode: reindex subcategories if url_key has change in category ([#122](https://github.com/DivanteLtd/magento2-vsbridge-indexer/issues/122)). +- Sort configurable options by option sort order. ([#129](https://github.com/DivanteLtd/magento2-vsbridge-indexer/pull/129)) ## [1.3.0] (2019.09.18) diff --git a/README.md b/README.md index f972b6c0..dcd8dd01 100644 --- a/README.md +++ b/README.md @@ -19,20 +19,13 @@ Sign up for a demo at https://vuestorefront.io/ (Vue Storefront integrated with - Install with composer ```json -composer config repositories.divante vcs https://github.com/DivanteLtd/magento2-vsbridge-indexer -composer require divante/magento2-vsbridge-indexer:dev-master +composer require divante/magento2-vsbridge-indexer ``` ## Installation/Getting Started - MSI support -- Install with composer changes from develop branch +- Install second module which will support MSI ```json -composer config repositories.divante vcs https://github.com/DivanteLtd/magento2-vsbridge-indexer -composer require divante/magento2-vsbridge-indexer:dev-develop -``` -- Install also second module which will support MSI -```json -composer config repositories.divante-msi vcs https://github.com/DivanteLtd/magento2-vsbridge-indexer-msi -composer require divante/magento2-vsbridge-indexer-msi:dev-develop +composer require divante/magento2-vsbridge-indexer-msi:0.1.0 ``` Not fully supported, few fields are exported to ES. From inventory indexer: @@ -256,6 +249,7 @@ php bin/magento indexer:reindex vsbridge_product_indexer php bin/magento indexer:reindex vsbridge_category_indexer php bin/magento indexer:reindex vsbridge_cms_block_indexer php bin/magento indexer:reindex vsbridge_cms_page_indexer +php bin/magento indexer:reindex vsbridge_review_indexer ``` @@ -283,6 +277,7 @@ Note: If a docker with ElasticSearch is disabled, Indexer will display error: "N - save/delete the static block - save/delete the static page - save/delete the attribute (deleting the attribute causes displaying “invalid” status for vsbridge products indexer). +- save/delete the review #### Update on Schedule Mode diff --git a/composer.json b/composer.json index 34d95205..4e48a900 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "name": "Agata", "email": "afirlejczyk@divante.pl" }], - "version": "1.3.0", + "version": "1.4.0", "keywords": [ "magento", "magento2", diff --git a/src/module-vsbridge-indexer-catalog/Index/Mapping/AbstractMapping.php b/src/module-vsbridge-indexer-catalog/Index/Mapping/AbstractMapping.php index 8ccf2470..50b5e8af 100644 --- a/src/module-vsbridge-indexer-catalog/Index/Mapping/AbstractMapping.php +++ b/src/module-vsbridge-indexer-catalog/Index/Mapping/AbstractMapping.php @@ -13,16 +13,17 @@ abstract class AbstractMapping /** * @var array */ - private $staticFieldMapping = [ - 'status' => FieldInterface::TYPE_INTEGER, - 'visibility' => FieldInterface::TYPE_INTEGER, - 'position' => FieldInterface::TYPE_LONG, - 'level' => FieldInterface::TYPE_INTEGER, - 'category_ids' => FieldInterface::TYPE_LONG, - 'sku' => FieldInterface::TYPE_KEYWORD, - 'url_path' => FieldInterface::TYPE_KEYWORD, - 'url_key' => FieldInterface::TYPE_KEYWORD, - ]; + private $staticFieldMapping; + + /** + * AbstractMapping constructor. + * + * @param array $staticFieldMapping + */ + public function __construct(array $staticFieldMapping) + { + $this->staticFieldMapping = $staticFieldMapping; + } /** * @var array diff --git a/src/module-vsbridge-indexer-catalog/Index/Mapping/Category.php b/src/module-vsbridge-indexer-catalog/Index/Mapping/Category.php index a028495d..82b73730 100644 --- a/src/module-vsbridge-indexer-catalog/Index/Mapping/Category.php +++ b/src/module-vsbridge-indexer-catalog/Index/Mapping/Category.php @@ -20,7 +20,6 @@ */ class Category extends AbstractMapping implements MappingInterface { - /** * @var array */ @@ -61,17 +60,20 @@ class Category extends AbstractMapping implements MappingInterface * @param GeneralMapping $generalMapping * @param CategoryChildAttributes $categoryChildAttributes * @param AttributeDataProvider $resourceModel + * @param array $staticFieldMapping */ public function __construct( EventManager $eventManager, GeneralMapping $generalMapping, CategoryChildAttributes $categoryChildAttributes, - AttributeDataProvider $resourceModel + AttributeDataProvider $resourceModel, + array $staticFieldMapping ) { $this->eventManager = $eventManager; $this->generalMapping = $generalMapping; $this->resourceModel = $resourceModel; $this->childAttributes = $categoryChildAttributes; + parent::__construct($staticFieldMapping); } /** diff --git a/src/module-vsbridge-indexer-catalog/Index/Mapping/Product.php b/src/module-vsbridge-indexer-catalog/Index/Mapping/Product.php index 7de16585..06f9e2fc 100644 --- a/src/module-vsbridge-indexer-catalog/Index/Mapping/Product.php +++ b/src/module-vsbridge-indexer-catalog/Index/Mapping/Product.php @@ -14,7 +14,6 @@ */ class Product extends AbstractMapping implements MappingInterface { - /** * @var EventManager */ @@ -52,12 +51,14 @@ public function __construct( EventManager $eventManager, GeneralMapping $generalMapping, ConfigurableAttributes $configurableAttributes, - AttributeDataProvider $resourceModel + AttributeDataProvider $resourceModel, + array $staticFieldMapping ) { $this->eventManager = $eventManager; $this->generalMapping = $generalMapping; $this->resourceModel = $resourceModel; $this->configurableAttributes = $configurableAttributes; + parent::__construct($staticFieldMapping); } /** diff --git a/src/module-vsbridge-indexer-catalog/Model/Attribute/LoadOptionById.php b/src/module-vsbridge-indexer-catalog/Model/Attribute/LoadOptionById.php new file mode 100644 index 00000000..1e90b3d8 --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Model/Attribute/LoadOptionById.php @@ -0,0 +1,54 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Model\Attribute; + +/** + * Class LoadOptionById + */ +class LoadOptionById +{ + + /** + * @var LoadOptions + */ + private $loadOptions; + + /** + * LoadOptionById constructor. + * + * @param LoadOptions $loadOptions + */ + public function __construct(LoadOptions $loadOptions) + { + $this->loadOptions = $loadOptions; + } + + /** + * @param string $attributeCode + * @param int $optionId + * @param int $storeId + * + * @return array + */ + public function execute(string $attributeCode, int $optionId, int $storeId): array + { + $options = $this->loadOptions->execute($attributeCode, $storeId); + + foreach ($options as $option) { + if ($optionId === (int)$option['value']) { + return $option; + } + } + + return []; + } +} diff --git a/src/module-vsbridge-indexer-catalog/Model/Attribute/LoadOptionLabelById.php b/src/module-vsbridge-indexer-catalog/Model/Attribute/LoadOptionLabelById.php deleted file mode 100644 index 1787f4d1..00000000 --- a/src/module-vsbridge-indexer-catalog/Model/Attribute/LoadOptionLabelById.php +++ /dev/null @@ -1,79 +0,0 @@ - - * @copyright 2019 Divante Sp. z o.o. - * @license See LICENSE_DIVANTE.txt for license details. - */ - -namespace Divante\VsbridgeIndexerCatalog\Model\Attribute; - -use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\AttributeDataProvider; -use Magento\Catalog\Model\ResourceModel\Eav\Attribute; - -/** - * Class LoadOptionLabelById - */ -class LoadOptionLabelById -{ - /** - * @var AttributeDataProvider - */ - private $attributeDataProvider; - - /** - * @var array - */ - private $optionsByAttribute = []; - - /** - * LoadLabelByOptionId constructor. - * - * @param AttributeDataProvider $attributeDataProvider - */ - public function __construct(AttributeDataProvider $attributeDataProvider) - { - $this->attributeDataProvider = $attributeDataProvider; - } - - /** - * @param string $attributeCode - * @param int $optionId - * @param int $storeId - * - * @return string - */ - public function execute(string $attributeCode, int $optionId, int $storeId): string - { - $attributeModel = $this->attributeDataProvider->getAttributeByCode($attributeCode); - $attributeModel->setStoreId($storeId); - $options = $this->loadOptions($attributeModel); - - foreach ($options as $option) { - if ($optionId === (int)$option['value']) { - return $option['label']; - } - } - - return ''; - } - - /** - * @param Attribute $attribute - * - * @return mixed - */ - private function loadOptions(Attribute $attribute) - { - $key = $attribute->getId() . '_' . $attribute->getStoreId(); - - if (!isset($this->optionsByAttribute[$key])) { - $this->optionsByAttribute[$key] = $attribute->getOptions(); - } - - return $this->optionsByAttribute[$key]; - } -} diff --git a/src/module-vsbridge-indexer-catalog/Model/Attribute/LoadOptions.php b/src/module-vsbridge-indexer-catalog/Model/Attribute/LoadOptions.php new file mode 100644 index 00000000..f7350907 --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Model/Attribute/LoadOptions.php @@ -0,0 +1,119 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Model\Attribute; + +use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\AttributeDataProvider; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection as OptionCollection; +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\CollectionFactory; + +use Magento\Eav\Model\Entity\Attribute\Source\Table as SourceTable; + +/** + * Class LoadOptionLabelById + */ +class LoadOptions +{ + /** + * @var AttributeDataProvider + */ + private $attributeDataProvider; + + /** + * @var CollectionFactory + */ + private $collectionFactory; + + /** + * @var OptionCollectionToArray + */ + private $optionCollectionToArray; + + /** + * @var array + */ + private $optionsByAttribute = []; + + /** + * LoadOptions constructor. + * + * @param CollectionFactory $collectionFactory + * @param OptionCollectionToArray $optionCollectionToArray + * @param AttributeDataProvider $attributeDataProvider + */ + public function __construct( + CollectionFactory $collectionFactory, + OptionCollectionToArray $optionCollectionToArray, + AttributeDataProvider $attributeDataProvider + ) { + $this->collectionFactory = $collectionFactory; + $this->attributeDataProvider = $attributeDataProvider; + $this->optionCollectionToArray = $optionCollectionToArray; + } + + /** + * @param string $attributeCode + * @param int $storeId + * + * @return string + */ + public function execute(string $attributeCode, int $storeId): array + { + $attributeModel = $this->attributeDataProvider->getAttributeByCode($attributeCode); + $attributeModel->setStoreId($storeId); + + return $this->loadOptions($attributeModel); + } + + /** + * @param Attribute $attribute + * + * @return array + */ + private function loadOptions(Attribute $attribute): array + { + $key = $attribute->getId() . '_' . $attribute->getStoreId(); + + if (!isset($this->optionsByAttribute[$key])) { + $source = $attribute->getSource(); + + if (SourceTable::class !== get_class($source) && + $source instanceof \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource + ) { + $options = $source->getAllOptions(); + } else { + $attributeId = $attribute->getAttributeId(); + $storeId = $attribute->getStoreId(); + + /** @var OptionCollection $options */ + $options = $this->collectionFactory->create(); + $options->setOrder('sort_order', 'asc'); + $options->setAttributeFilter($attributeId)->setStoreFilter($storeId); + $options = $this->toOptionArray($options); + } + + $this->optionsByAttribute[$key] = $options; + } + + return $this->optionsByAttribute[$key]; + } + + /** + * @param OptionCollection $collection + * + * @return array + */ + private function toOptionArray(OptionCollection $collection): array + { + return $this->optionCollectionToArray->execute($collection); + } +} diff --git a/src/module-vsbridge-indexer-catalog/Model/Attribute/OptionCollectionToArray.php b/src/module-vsbridge-indexer-catalog/Model/Attribute/OptionCollectionToArray.php new file mode 100644 index 00000000..35b6a95c --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Model/Attribute/OptionCollectionToArray.php @@ -0,0 +1,58 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Model\Attribute; + +use Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection as OptionCollection; + +/** + * Class OptionCollectionToArray + */ +class OptionCollectionToArray +{ + + /** + * @param OptionCollection $collection + * + * @return array + */ + public function execute(OptionCollection $collection): array + { + $res = []; + $additional['value'] = 'option_id'; + $additional['label'] = 'value'; + $additional['sort_order'] = 'sort_order'; + + foreach ($collection as $item) { + $data = []; + + foreach ($additional as $code => $field) { + $value = $item->getData($field); + + if ('sort_order' === $field) { + $value = (int)$value; + } + + if ('option_id' === $field) { + $value = (string)$value; + } + + $data[$code] = $value; + } + + if ($data) { + $res[] = $data; + } + } + + return $res; + } +} diff --git a/src/module-vsbridge-indexer-catalog/Model/Attribute/SortValues.php b/src/module-vsbridge-indexer-catalog/Model/Attribute/SortValues.php new file mode 100644 index 00000000..a0c7392d --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Model/Attribute/SortValues.php @@ -0,0 +1,48 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Model\Attribute; + +/** + * Class SortValues + */ +class SortValues +{ + /** + * @param array $options + * + * @return array + */ + public function execute(array $options) + { + usort($options, [$this, 'sortOptions']); + + return $options; + } + + /** + * @param array $a + * @param array $b + * + * @return int + */ + public function sortOptions($a, $b) + { + $aSizePos = $a['sort_order'] ?? 0; + $bSizePos = $b['sort_order'] ?? 0; + + if ($aSizePos === $bSizePos) { + return 0; + } + + return ($aSizePos > $bSizePos) ? 1 : -1; + } +} diff --git a/src/module-vsbridge-indexer-catalog/Model/ConfigurableProcessor/GetConfigurableOptions.php b/src/module-vsbridge-indexer-catalog/Model/ConfigurableProcessor/GetConfigurableOptions.php new file mode 100644 index 00000000..d9bcc41d --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Model/ConfigurableProcessor/GetConfigurableOptions.php @@ -0,0 +1,72 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Model\ConfigurableProcessor; + +use Divante\VsbridgeIndexerCatalog\Model\Attribute\LoadOptionById; +use Divante\VsbridgeIndexerCatalog\Model\Attribute\SortValues; + +/** + * Class GetConfigurableOptions + */ +class GetConfigurableOptions +{ + /** + * @var LoadOptionById + */ + private $loadOptionById; + + /** + * @var SortValues + */ + private $sortValues; + + /** + * GetConfigurableOptions constructor. + * + * @param LoadOptionById $loadOptions + * @param SortValues $sortValues + */ + public function __construct(LoadOptionById $loadOptions, SortValues $sortValues) + { + $this->loadOptionById = $loadOptions; + $this->sortValues = $sortValues; + } + + /** + * @param string $attributeCode + * @param int $storeId + * @param array $configurableChildren + * + * @return array + */ + public function execute(string $attributeCode, int $storeId, array $configurableChildren): array + { + $values = []; + + foreach ($configurableChildren as $child) { + if (isset($child[$attributeCode])) { + $value = $child[$attributeCode]; + + if (isset($value)) { + $values[] = (int) $value; + } + } + } + + $values = array_values(array_unique($values)); + $options = []; + + foreach ($values as $value) { + $option = $this->loadOptionById->execute($attributeCode, $value, $storeId); + $options[] = $option; + } + + return $this->sortValues->execute($options); + } +} diff --git a/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Attribute/Options.php b/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Attribute/Options.php index f520cd51..93222694 100644 --- a/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Attribute/Options.php +++ b/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Attribute/Options.php @@ -9,12 +9,8 @@ namespace Divante\VsbridgeIndexerCatalog\Model\Indexer\DataProvider\Attribute; use Divante\VsbridgeIndexerCore\Api\DataProviderInterface; -use Magento\Eav\Model\Entity\AttributeFactory; -use Magento\Eav\Model\Entity\Attribute\Source\Table as SourceTable; +use Divante\VsbridgeIndexerCatalog\Model\Attribute\LoadOptions; use Magento\Eav\Model\ResourceModel\Entity\Attribute as EntityResource; -use Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\Collection as OptionCollection; -use Magento\Eav\Model\ResourceModel\Entity\Attribute\Option\CollectionFactory; -use Magento\Framework\Validator\UniversalFactory; /** * Class Options @@ -23,19 +19,9 @@ class Options implements DataProviderInterface { /** - * @var UniversalFactory + * @var LoadOptions */ - private $universalFactory; - - /** - * @var CollectionFactory - */ - private $collectionFactory; - - /** - * @var AttributeFactory - */ - private $attributeFactory; + private $loadOptions; /** * @var EntityResource @@ -45,20 +31,14 @@ class Options implements DataProviderInterface /** * Options constructor. * - * @param CollectionFactory $collectionFactory - * @param AttributeFactory $attributeFactory - * @param UniversalFactory $universalFactory + * @param LoadOptions $loadOptions * @param EntityResource $entityResource */ public function __construct( - CollectionFactory $collectionFactory, - AttributeFactory $attributeFactory, - UniversalFactory $universalFactory, + LoadOptions $loadOptions, EntityResource $entityResource ) { - $this->attributeFactory = $attributeFactory; - $this->collectionFactory = $collectionFactory; - $this->universalFactory = $universalFactory; + $this->loadOptions = $loadOptions; $this->entityAttributeResource = $entityResource; } @@ -93,73 +73,9 @@ public function addData(array $indexData, $storeId) */ public function getAttributeOptions(array $attributeData, $storeId) { - $values = []; - $source = (string)$attributeData['source_model']; - $attributeId = $attributeData['attribute_id']; - - if ('' !== $source && SourceTable::class !== $source) { - $sourceModel = $this->universalFactory->create($source); - - if (false !== $sourceModel) { - if ($sourceModel instanceof \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource) { - ///** @var Attribute $attribute */ - $attribute = $this->attributeFactory->create($attributeData); - $attribute->setStoreId($storeId); - $sourceModel->setAttribute($attribute); - } - - $values = $sourceModel->getAllOptions(false); - } - } else { - /** @var OptionCollection $options */ - $options = $this->collectionFactory->create(); - $options->setOrder('sort_order', 'asc'); - $options->setAttributeFilter($attributeId)->setStoreFilter($storeId); - $values = $this->toOptionArray($options); - } - - return $values; + return $this->loadOptions->execute($attributeData['attribute_code'], $storeId); } - - /** - * @param OptionCollection $collection - * - * @param array $additional - * - * @return array - */ - public function toOptionArray(OptionCollection $collection, array $additional = []) - { - $res = []; - $additional['value'] = 'option_id'; - $additional['label'] = 'value'; - $additional['sort_order'] = 'sort_order'; - - foreach ($collection as $item) { - $data = []; - - foreach ($additional as $code => $field) { - $value = $item->getData($field); - - if ($field === 'sort_order') { - $value = (int)$value; - } - - if ($field === 'option_id') { - $value = (string)$value; - } - - $data[$code] = $value; - } - - if ($data) { - $res[] = $data; - } - } - - return $res; - } - + /** * @param array $attributeData * 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 99417139..c9d22614 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,7 +8,7 @@ namespace Divante\VsbridgeIndexerCatalog\Model\Indexer\DataProvider\Product; -use Divante\VsbridgeIndexerCatalog\Model\Attribute\LoadOptionLabelById; +use Divante\VsbridgeIndexerCatalog\Model\ConfigurableProcessor\GetConfigurableOptions; use Divante\VsbridgeIndexerCatalog\Model\Attributes\ConfigurableAttributes; use Divante\VsbridgeIndexerCatalog\Model\InventoryProcessor; use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\AttributeDataProvider; @@ -77,9 +77,9 @@ class ConfigurableData implements DataProviderInterface private $tierPriceProcessor; /** - * @var LoadOptionLabelById + * @var GetConfigurableOptions */ - private $loadOptionLabelById; + private $configurableProcessor; /** * ConfigurableData constructor. @@ -88,7 +88,7 @@ class ConfigurableData implements DataProviderInterface * @param ConfigurableResource $configurableResource * @param AttributeDataProvider $attributeResource * @param LoadInventoryInterface $loadInventory - * @param LoadOptionLabelById $loadOptionLabelById + * @param GetConfigurableOptions $configurableProcessor * @param ConfigurableAttributes $configurableAttributes * @param TierPriceProcessor $tierPriceProcessor * @param InventoryProcessor $inventoryProcessor @@ -98,7 +98,7 @@ public function __construct( ConfigurableResource $configurableResource, AttributeDataProvider $attributeResource, LoadInventoryInterface $loadInventory, - LoadOptionLabelById $loadOptionLabelById, + GetConfigurableOptions $configurableProcessor, ConfigurableAttributes $configurableAttributes, TierPriceProcessor $tierPriceProcessor, InventoryProcessor $inventoryProcessor @@ -109,7 +109,7 @@ public function __construct( $this->loadInventory = $loadInventory; $this->inventoryProcessor = $inventoryProcessor; $this->tierPriceProcessor = $tierPriceProcessor; - $this->loadOptionLabelById = $loadOptionLabelById; + $this->configurableProcessor = $configurableProcessor; $this->configurableAttributes = $configurableAttributes; } @@ -140,7 +140,7 @@ public function addData(array $indexData, $storeId) * Skip exporting configurable products without options */ if (!empty($productDTO['configurable_options'])) { - $productsList[$productId] = $this->prepareConfigurableProduct($productDTO);; + $productsList[$productId] = $this->prepareConfigurableProduct($productDTO); } } @@ -220,7 +220,7 @@ private function getRequiredChildrenAttributes() /** * Apply attributes to product variants + extra options for products necessary for vsf * @param array $productDTO - * @param $storeId + * @param int $storeId * * @return array * @throws \Exception @@ -231,6 +231,8 @@ private function applyConfigurableOptions(array $productDTO, $storeId) $productAttributeOptions = $this->configurableResource->getProductConfigurableAttributes($productDTO); + $productDTO['configurable_children'] = $configurableChildren; + foreach ($productAttributeOptions as $productAttribute) { $attributeCode = $productAttribute['attribute_code']; @@ -238,26 +240,19 @@ private function applyConfigurableOptions(array $productDTO, $storeId) $productDTO[$attributeCode . '_options'] = []; } - $values = []; - - foreach ($configurableChildren as $child) { - if (isset($child[$attributeCode])) { - $value = $child[$attributeCode]; - - if (isset($value)) { - $values[] = (int) $value; - } - } - } + $options = $this->configurableProcessor->execute( + $attributeCode, + $storeId, + $configurableChildren + ); - $productDTO['configurable_children'] = $configurableChildren; - $values = array_values(array_unique($values)); + $values = []; - foreach ($values as $value) { - $label = $this->loadOptionLabelById->execute($attributeCode, $value, $storeId); + foreach ($options as $option) { + $values[] = (int)$option['value']; $productAttribute['values'][] = [ - 'value_index' => $value, - 'label' => $label, + 'value_index' => $option['value'], + 'label' => $option['label'], ]; } diff --git a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/AbstractEavAttributes.php b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/AbstractEavAttributes.php index ddb521c2..b06b8b15 100644 --- a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/AbstractEavAttributes.php +++ b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/AbstractEavAttributes.php @@ -175,6 +175,7 @@ private function prepareValues(array $values) * @param mixed $value * * @return mixed + * @SuppressWarnings("unused") */ private function parseValue($value) { diff --git a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Category.php b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Category.php index 022e7747..6780233b 100644 --- a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Category.php +++ b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Category.php @@ -170,6 +170,28 @@ public function getParentIds(array $categoryIds) return array_unique($parentIds); } + /** + * @param int $categoryId + * + * @return int[] + * @throws \Exception + */ + public function getAllSubCategories(int $categoryId): array + { + $metaData = $this->categoryMetaData->get(); + $entityField = $metaData->getIdentifierField(); + $connection = $this->getConnection(); + $select = $connection->select()->from( + ['entity' => $metaData->getEntityTable()], + [$entityField] + ); + + $catIdExpr = $connection->quote("%/{$categoryId}/%"); + $select->where("path like {$catIdExpr}"); + + return $connection->fetchCol($select); + } + /** * @return \Magento\Framework\DB\Adapter\AdapterInterface */ diff --git a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product.php b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product.php index c1721a5c..000fcd5c 100644 --- a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product.php +++ b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product.php @@ -9,13 +9,13 @@ namespace Divante\VsbridgeIndexerCatalog\Model\ResourceModel; use Divante\VsbridgeIndexerCatalog\Api\Data\CatalogConfigurationInterface; +use Divante\VsbridgeIndexerCatalog\Model\ProductMetaData; use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\AttributeDataProvider; use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Helper as DbHelper; use Magento\Framework\DB\Select; use Magento\Store\Model\StoreManagerInterface; -use Divante\VsbridgeIndexerCatalog\Model\ProductMetaData; /** * Class Product @@ -97,11 +97,8 @@ public function __construct( */ public function getProducts($storeId = 1, array $productIds = [], $fromId = 0, $limit = 1000) { - $select = $this->getConnection()->select() - ->from( - ['entity' => $this->productMetaData->get()->getEntityTable()], - $this->getRequiredColumns() - ); + $select = $this->prepareBaseProductSelect($this->getRequiredColumns(), $storeId); + $select = $this->addProductTypeFilter($select, $storeId); if (!empty($productIds)) { $select->where('entity.entity_id IN (?)', $productIds); @@ -110,16 +107,34 @@ public function getProducts($storeId = 1, array $productIds = [], $fromId = 0, $ $select->limit($limit); $select->where('entity.entity_id > ?', $fromId); $select->order('entity.entity_id ASC'); + + return $this->getConnection()->fetchAll($select); + } + + /** + * @param array $requiredColumns + * @param int $storeId + * + * @return Select + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function prepareBaseProductSelect(array $requiredColumns, int $storeId) + { + $select = $this->getConnection()->select() + ->from( + ['entity' => $this->productMetaData->get()->getEntityTable()], + $requiredColumns + ); + $select = $this->addStatusFilter($select, $storeId); $select = $this->addWebsiteFilter($select, $storeId); - $select = $this->addProductTypeFilter($select, $storeId); - return $this->getConnection()->fetchAll($select); + return $select; } /** * @return array - * @throws \Exception */ private function getRequiredColumns() { @@ -162,10 +177,7 @@ public function loadChildrenProducts(array $parentIds, $storeId) $columns[] = $linkField; } - $select = $this->getConnection()->select()->from( - ['entity' => $this->productMetaData->get()->getEntityTable()], - $columns - ); + $select = $this->prepareBaseProductSelect($columns, $storeId); $select->join( ['link_table' => $this->resourceConnection->getTableName('catalog_product_super_link')], @@ -173,13 +185,10 @@ public function loadChildrenProducts(array $parentIds, $storeId) [] ); - $select = $this->addStatusFilter($select, $storeId); - $select->where('link_table.parent_id IN (?)', $parentIds); $select->group('entity_id'); $this->dbHelper->addGroupConcatColumn($select, 'parent_ids', 'parent_id'); - $select = $this->addWebsiteFilter($select, $storeId); return $this->getConnection()->fetchAll($select); } @@ -256,29 +265,6 @@ public function getRelationsByChild(array $childrenIds) return $this->getConnection()->fetchCol($select); } - /** - * @param int $storeId - * @param array $productIds - * - * @return array - * - * @throws \Magento\Framework\Exception\LocalizedException - * @throws \Magento\Framework\Exception\NoSuchEntityException - */ - public function getEnableProductIds($storeId, array $productIds) - { - $select = $this->getConnection()->select() - ->from(['e' => $this->resourceConnection->getTableName('catalog_product_entity')]); - $select->where('e.entity_id IN (?)', $productIds); - $select->order('e.entity_id ASC'); - $select = $this->addStatusFilter($select, $storeId); - $select = $this->addWebsiteFilter($select, $storeId); - $select->reset(Select::COLUMNS); - $select->columns(['entity_id']); - - return $this->getConnection()->fetchCol($select); - } - /** * @param \Magento\Framework\DB\Select $select * @param int $storeId diff --git a/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Category/Save/UpdateCategoryDataPlugin.php b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Category/Save/UpdateCategoryDataPlugin.php index 6493ac48..1de8e6d7 100644 --- a/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Category/Save/UpdateCategoryDataPlugin.php +++ b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Category/Save/UpdateCategoryDataPlugin.php @@ -9,6 +9,8 @@ namespace Divante\VsbridgeIndexerCatalog\Plugin\Indexer\Category\Save; use Divante\VsbridgeIndexerCatalog\Model\Indexer\CategoryProcessor; +use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Category as CategoryResourceModel; + use Magento\Catalog\Model\Category; /** @@ -16,19 +18,28 @@ */ class UpdateCategoryDataPlugin { + /** + * @var CategoryResourceModel + */ + private $resourceModel; + /** * @var CategoryProcessor */ private $categoryProcessor; /** - * UpdateProductData constructor. + * UpdateCategoryDataPlugin constructor. * + * @param CategoryResourceModel $resourceModel * @param CategoryProcessor $processor */ - public function __construct(CategoryProcessor $processor) - { + public function __construct( + CategoryResourceModel $resourceModel, + CategoryProcessor $processor + ) { $this->categoryProcessor = $processor; + $this->resourceModel = $resourceModel; } /** @@ -40,6 +51,17 @@ public function __construct(CategoryProcessor $processor) */ public function afterReindex(Category $category) { - $this->categoryProcessor->reindexRow($category->getId()); + $categoryIds = []; + $originalUrlKey = $category->getOrigData('url_key'); + $urlKey = $category->getData('url_key'); + $categoryId = (int) $category->getId(); + + if (!$category->isObjectNew() && $originalUrlKey !== $urlKey) { + $categoryIds = $this->resourceModel->getAllSubCategories($categoryId); + } + + $categoryIds[] = $categoryId; + + $this->categoryProcessor->reindexList($categoryIds); } } diff --git a/src/module-vsbridge-indexer-catalog/etc/di.xml b/src/module-vsbridge-indexer-catalog/etc/di.xml index b67f5743..adb17d3b 100644 --- a/src/module-vsbridge-indexer-catalog/etc/di.xml +++ b/src/module-vsbridge-indexer-catalog/etc/di.xml @@ -137,4 +137,18 @@ + + + + Divante\VsbridgeIndexerCore\Api\Mapping\FieldInterface::TYPE_INTEGER + Divante\VsbridgeIndexerCore\Api\Mapping\FieldInterface::TYPE_INTEGER + Divante\VsbridgeIndexerCore\Api\Mapping\FieldInterface::TYPE_LONG + Divante\VsbridgeIndexerCore\Api\Mapping\FieldInterface::TYPE_INTEGER + Divante\VsbridgeIndexerCore\Api\Mapping\FieldInterface::TYPE_LONG + Divante\VsbridgeIndexerCore\Api\Mapping\FieldInterface::TYPE_KEYWORD + Divante\VsbridgeIndexerCore\Api\Mapping\FieldInterface::TYPE_KEYWORD + Divante\VsbridgeIndexerCore\Api\Mapping\FieldInterface::TYPE_KEYWORD + + + diff --git a/src/module-vsbridge-indexer-core/Console/Command/RebuildEsIndexCommand.php b/src/module-vsbridge-indexer-core/Console/Command/RebuildEsIndexCommand.php index 4b9e5553..78a56c2a 100644 --- a/src/module-vsbridge-indexer-core/Console/Command/RebuildEsIndexCommand.php +++ b/src/module-vsbridge-indexer-core/Console/Command/RebuildEsIndexCommand.php @@ -11,21 +11,21 @@ use Divante\VsbridgeIndexerCatalog\Model\Indexer\ProductCategoryProcessor; use Divante\VsbridgeIndexerCore\Indexer\StoreManager; use Divante\VsbridgeIndexerCore\Index\IndexOperations; -use Magento\Backend\App\Area\FrontNameResolver; -use Magento\Framework\Indexer\IndexerInterface; +use Magento\Framework\App\ObjectManagerFactory; use Magento\Framework\Console\Cli; use Magento\Framework\Exception\LocalizedException; -use Magento\Framework\Indexer\IndexerRegistry; +use Magento\Framework\Indexer\IndexerInterface; +use Magento\Indexer\Console\Command\AbstractIndexerCommand; use Magento\Store\Api\Data\StoreInterface; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use Magento\Store\Model\StoreManagerInterface as StoreManagerInterface; /** * Class IndexerReindexCommand */ -class RebuildEsIndexCommand extends Command +class RebuildEsIndexCommand extends AbstractIndexerCommand { const INPUT_STORE = 'store'; const INPUT_ALL_STORES = 'all'; @@ -33,11 +33,6 @@ class RebuildEsIndexCommand extends Command const INDEX_IDENTIFIER = 'vue_storefront_catalog'; - /** - * @var \Magento\Indexer\Model\Indexer\CollectionFactory - */ - private $collectionFactory; - /** * @var IndexOperations */ @@ -46,40 +41,21 @@ class RebuildEsIndexCommand extends Command /** * @var StoreManager */ - private $storeManager; - - /** - * @var IndexerRegistry - */ - private $indexerRegistry; + private $indexerStoreManager; /** - * @var \Magento\Framework\App\State + * @var StoreManagerInterface */ - private $state; + private $storeManager; /** - * Construct + * RebuildEsIndexCommand constructor. * - * @param \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry - * @param IndexOperations\Proxy $indexOperations - * @param StoreManager\Proxy $storeManager - * @param \Magento\Framework\App\State\Proxy $state - * @param \Magento\Indexer\Model\Indexer\CollectionFactory\Proxy $collectionFactory + * @param ObjectManagerFactory $objectManagerFactory */ - public function __construct( - IndexerRegistry $indexerRegistry, - IndexOperations $indexOperations, - StoreManager $storeManager, - \Magento\Framework\App\State $state, - \Magento\Indexer\Model\Indexer\CollectionFactory $collectionFactory - ) { - $this->indexerRegistry = $indexerRegistry; - $this->collectionFactory = $collectionFactory; - $this->indexOperations = $indexOperations; - $this->storeManager = $storeManager; - $this->state = $state; - parent::__construct(); + public function __construct(ObjectManagerFactory $objectManagerFactory) + { + parent::__construct($objectManagerFactory); } /** @@ -120,33 +96,28 @@ protected function configure() */ protected function execute(InputInterface $input, OutputInterface $output) { + $this->initObjectManager(); $output->setDecorated(true); $storeId = $input->getOption(self::INPUT_STORE); $allStores = $input->getOption(self::INPUT_ALL_STORES); $deleteIndex = $input->getOption(self::INPUT_DELETE_INDEX); if ($storeId) { - $stores = $this->storeManager->getStores($storeId); + $store = $this->getStoreManager()->getStore($storeId); + $output->writeln("Reindexing all VS indexes for store " . $store->getName() . "..."); - if (!empty($stores)) { - /** @var \Magento\Store\Api\Data\StoreInterface $store */ - $store = $stores[0]; - $output->writeln("Reindexing all VS indexes for store " . $store->getName() . "..."); + $returnValue = $this->reindexStore($store, $deleteIndex, $output); - $this->setAreaCode(); - $returnValue = $this->reindexStore($store, $deleteIndex, $output); + $output->writeln("Reindexing has completed!"); - $output->writeln("Reindexing has completed!"); + return $returnValue; - return $returnValue; - } } elseif ($allStores) { $output->writeln("Reindexing all stores..."); - $this->setAreaCode(); $returnValues = []; /** @var \Magento\Store\Api\Data\StoreInterface $store */ - foreach ($this->storeManager->getStores() as $store) { + foreach ($this->getStoreManager()->getStores() as $store) { $output->writeln("Reindexing store " . $store->getName() . "..."); $returnValues[] = $this->reindexStore($store, $deleteIndex, $output); } @@ -161,6 +132,42 @@ protected function execute(InputInterface $input, OutputInterface $output) } } + /** + * @return StoreManagerInterface + */ + private function getStoreManager() + { + if (null === $this->storeManager) { + $this->storeManager = $this->getObjectManager()->get(StoreManagerInterface::class); + } + + return $this->storeManager; + } + + /** + * @return StoreManager + */ + private function getIndexerStoreManager() + { + if (null === $this->indexerStoreManager) { + $this->indexerStoreManager = $this->getObjectManager()->get(StoreManager::class); + } + + return $this->indexerStoreManager; + } + + /** + * @return IndexOperations + */ + private function getIndexOperations() + { + if (null === $this->indexOperations) { + $this->indexOperations = $this->getObjectManager()->get(IndexOperations::class); + } + + return $this->indexOperations; + } + /** * Reindex each vsbridge index for the specified store * @@ -172,10 +179,12 @@ protected function execute(InputInterface $input, OutputInterface $output) */ private function reindexStore(StoreInterface $store, bool $deleteIndex, OutputInterface $output) { + $this->getIndexerStoreManager()->setLoadedStores([$store]); + if ($deleteIndex) { $output->writeln("Deleting and recreating the index first..."); - $this->indexOperations->deleteIndex(self::INDEX_IDENTIFIER, $store); - $this->indexOperations->createIndex(self::INDEX_IDENTIFIER, $store); + $this->getIndexOperations()->deleteIndex(self::INDEX_IDENTIFIER, $store); + $this->getIndexOperations()->createIndex(self::INDEX_IDENTIFIER, $store); } $returnValue = Cli::RETURN_FAILURE; @@ -202,24 +211,14 @@ private function reindexStore(StoreInterface $store, bool $deleteIndex, OutputIn return $returnValue; } - /** - * @return void - */ - private function setAreaCode() - { - try { - $this->state->setAreaCode(FrontNameResolver::AREA_CODE); - } catch (\Exception $e) { - } - } - /** * @return IndexerInterface[] */ - protected function getIndexers() + private function getIndexers() { /** @var IndexerInterface[] */ - $indexers = $this->collectionFactory->create()->getItems(); + $indexers = $this->getAllIndexers(); + unset($indexers[ProductCategoryProcessor::INDEXER_ID]); $vsbridgeIndexers = []; @@ -231,4 +230,12 @@ protected function getIndexers() return $vsbridgeIndexers; } + + /** + * Initiliaze object manager + */ + private function initObjectManager() + { + $this->getObjectManager(); + } } diff --git a/src/module-vsbridge-indexer-core/Indexer/StoreManager.php b/src/module-vsbridge-indexer-core/Indexer/StoreManager.php index 9a1e281f..3a397983 100644 --- a/src/module-vsbridge-indexer-core/Indexer/StoreManager.php +++ b/src/module-vsbridge-indexer-core/Indexer/StoreManager.php @@ -52,7 +52,6 @@ public function __construct( * @return array|\Magento\Store\Api\Data\StoreInterface[] * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function getStores($storeId = null) { if (null === $this->loadedStores) { @@ -80,4 +79,12 @@ public function getStores($storeId = null) return $this->loadedStores; } + + /** + * @param array $stores + */ + public function setLoadedStores(array $stores) + { + $this->loadedStores = $stores; + } } diff --git a/src/module-vsbridge-indexer-core/etc/di.xml b/src/module-vsbridge-indexer-core/etc/di.xml index 9a079c59..8f3dd332 100644 --- a/src/module-vsbridge-indexer-core/etc/di.xml +++ b/src/module-vsbridge-indexer-core/etc/di.xml @@ -67,14 +67,4 @@ - - - - - Divante\VsbridgeIndexerCore\Index\IndexOperations\Proxy - Divante\VsbridgeIndexerCore\Indexer\StoreManager\Proxy - Magento\Framework\App\State\Proxy - Magento\Indexer\Model\Indexer\CollectionFactory\Proxy - - diff --git a/src/module-vsbridge-indexer-tax/ResourceModel/Rates.php b/src/module-vsbridge-indexer-tax/ResourceModel/Rates.php index 6e9839a1..be077a73 100644 --- a/src/module-vsbridge-indexer-tax/ResourceModel/Rates.php +++ b/src/module-vsbridge-indexer-tax/ResourceModel/Rates.php @@ -50,7 +50,7 @@ public function loadTaxRates(array $ruleIds) ->where('tax_calculation_rule_id IN (?)', $ruleIds); $select->distinct(true); - return $this->getConnection()->fetchAssoc($select); + return $this->getConnection()->fetchAll($select); } /**