diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 70dd4f98..7810c756 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -1,6 +1,20 @@ # Changelog -## [Unreleased] + +## [1.1.0] (2019.07.10) + +### Fixed +- Fix bug causing empty product URL paths - [@indiebytes](https://github.com/indiebytes) ([#63](https://github.com/DivanteLtd/magento2-vsbridge-indexer/issues/63)) +- Changed ElasticSearch password to be obscured, encrypted, and considered sensitive (will dump to `env.php` instead of `config.php`) - [@rain2o](https://github.com/rain2o) ([#69](https://github.com/DivanteLtd/magento2-vsbridge-indexer/issues/69)) +- Fix exporting values for multiselect option arrays as integers (instead of strings) +- Fix getting stock_status value for products +- Magento Commerce - fix getting configurable_children + +### Changed/Improved +- Change mapping for "category.name" field in product type +- Remove dependency from catalog_product_price and cataloginventory_stock indexer. On product save multiple request has been send +- Change "Use Short Catalog Urls" setting option to "Use Catalog Url Keys" +- Add option to generate product/category slug base on Magento Url Key and ID. By default slug (and url_key) field is generated base on NAME and ID. ### Added - Adding support for video data. Small change will be needed in VSF #19 @@ -9,6 +23,44 @@ There is not need really, VSF only has to know if `product_count > 0`, so if you category will be visible in menu sidebar. - Add support for reviews. Reviews are exported without ratings (VSF does not support ratings for now) - Add support for custom options. +- Add option to enable/disable exporting data to ES. +- Add ProductCategory indexer to partially update product data in ES (category, category_ids fields). Trigger after changing products positions in category. +- Add the ability to choose between Store ID and Store Code to be used at the end of index names. +- Add label for configurable option value. **Note:** When You modify any configurable attribute label or option label in Magento You should reindex all products manually. +```json +{ + "attribute_id": 93, + "attribute_code": "color", + "label": "Color", + "values": [ + { + "value_index": 61, + "label": "Gray" + }, + { + "value_index": 66, + "label": "Purple" + }, + { + "value_index": 69, + "label": "Yellow" + } + ] +} +``` +- Add new command ```php bin/magento vsbridge:reindex``` which will run all Magento indices which names start with "vsbridge_" (experimental). +```php +Description: + Rebuild indexer in ES. + +Usage: + vsbridge:reindex [options] + +Options: + --store=STORE Store ID + --delete-index Delete previous index and create new one (with new mapping) +``` + ## [1.0.0] (2019.04.03) First version diff --git a/README.md b/README.md index 54c90641..da7470a0 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,48 @@ composer config repositories.divante vcs https://github.com/DivanteLtd/magento2- composer require divante/magento2-vsbridge-indexer:dev-master ``` +## Installation/Getting Started - MSI support +- Install with composer changes from develop branch +```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 +``` +Not fully supported, few fields are exported to ES. +From inventory indexer: + -- qty => qty, + -- is_salable => is_in_stock/stock_status + +#### Example + +Website 1 +```json +{ + "sku": "24-MB01", + "stock": { + "qty": 100, + "is_in_stock": false, + "stock_status": 0 + } +} +``` + +Website 2 +```json +{ + "sku": "24-MB01", + "stock": { + "qty": 73, + "is_in_stock": true, + "stock_status": 1 + } +} +``` + ```php php bin/magento setup:upgrade ``` @@ -30,7 +72,12 @@ php bin/magento setup:upgrade ### Configuration -Go to the new ‘Indexer’ section (Stores → Configuration → Vuestorefront → Indexer), available now in the in the Magento Panel, and configure it in the listed areas: +Go to the new ‘Indexer’ section (Stores → Configuration → Vuestorefront → Indexer), available now in the in the Magento Panel, and configure it in the listed areas: +1. General settings → Enable VS Bridge + + Enable to export data to elasticsearch. By default indexing is disable. + + ![](docs/images/config-general-enable.png) 1. General settings → List of stores to reindex @@ -49,9 +96,57 @@ Go to the new ‘Indexer’ section (Stores → Configuration → Vuestorefront Batch Indexing Size → select size of packages by which you intend to send data to ElasticSrearch. Depending on the project you might need to adjust package size to the number of products, attributes, configurable products variation, etc). By default Batch, Indexing Size is set up for 1000. Indicies settings - Index Name Prefix → define prefixes for ElasticSearch indexes. The panel allows adding prefix only to the catalog name e.g.: "vue_storefront_catalog". For each store (store view) index name is generated on the base of defined prefix and ID. Aliases cannot be created. - Example: When we define following indexes: "vue_storefront_catalog_1", "vue_storefront_catalog_2", "vue_storefront_catalog_3", their name will remain unchanged, and only product and category names will be updated. + Index Name Prefix → define prefixes for ElasticSearch indexes. The panel allows adding prefix only to the catalog name e.g.: "vue_storefront_catalog". For each store (store view) index name is generated on the base of defined prefix and either ID or Store Code. Aliases cannot be created. + Example: When we define following indexes: "vue_storefront_catalog_1", "vue_storefront_catalog_2", "vue_storefront_catalog_3". Important: It is crucial to update this configuration in the VSF and VSF-API (one change at the beginning of the whole configuration process). + + Index Identifier → defines the unique store identifier to append to the ElasticSearch indexes. The default value is ID which will append the Store ID to the index name e.g.: "vue_storefront_catalog_1". You can choose to change this to Store Code which will add the Store Code to the index name e.g.: "vue_storefront_catalog_storecode". + + Example with Store ID + + VSF config (base on default index prefix name: vue_storefront_magento) + ```json + "elasticsearch": { + "httpAuth": "", + "host": "localhost:8080/api/catalog", + "index": "vue_storefront_magento_1" //index for store view with id 1 + } + ``` + + VSF-API config + ```json + "elasticsearch": { + "host": "localhost", + "port": 9200, + "user": "elastic", + "password": "changeme", + "indices": [ + "vue_storefront_magento_1" //index for store view with id 1 + ], + ``` + + Example with Store Code + + VSF config (base on default index prefix name: vue_storefront_magento) + ```json + "elasticsearch": { + "httpAuth": "", + "host": "localhost:8080/api/catalog", + "index": "vue_storefront_magento_en_us" //index for store view with code "en_us" + } + ``` + + VSF-API config + ```json + "elasticsearch": { + "host": "localhost", + "port": 9200, + "user": "elastic", + "password": "changeme", + "indices": [ + "vue_storefront_magento_en_us" //index for store view with store code "en_us" + ], + ``` ![](docs/images/config-indices.png) @@ -69,7 +164,9 @@ Go to the new ‘Indexer’ section (Stores → Configuration → Vuestorefront 1. Catalog Settings - Use Short Catalog Urls → by default this option is disabled. The short Catalog Urls must be aligned with the VSF configuration. After any changes in the VSF configuration, the configuration in the Magento Panel must be updated and all products and categories indexed anew. + Use Catalog Url Keys → by default this option is disabled. Use Magento Url Key attribute for url_key and slug field (for products and categories). Url Keys have to be unique + + Use Magento Url Key and ID to generate slug for VSF -> by default slug (and url_key) field is generated base on product/category NAME and ID Sync Tier Prices → by default this option is disabled. Used to sync products tier prices. @@ -103,6 +200,12 @@ Recommended for smaller databases. In the case of big databases it is better to php bin/magento indexer:reindex ``` +or +```php +php bin/magento vsbridge:reindex --store=[STORE_ID] +php bin/magento vsbridge:reindex --store=1 +``` + Note: If a docker with ElasticSearch is disabled, Indexer will display error: "No alive nodes found in your cluster". #### Update on Save Mode @@ -121,8 +224,9 @@ Note: If a docker with ElasticSearch is disabled, Indexer will display error: "N ### Compatibility +-- Vue Storefront >= 1.4.4 Module was tested on: - -- Magento Community version 2.2.0 and 2.2.4. It should perform without any issues on all Magento 2.2.x versions. + -- Magento Community version 2.2.7 It should perform without any issues on Magento 2.2.6 and above versions. -- Magento Enterprise version 2.3.0. The bridge indexer cannot be installed on lower versions of Magento Enterprise. Module was not tested on version 2.3. @@ -130,7 +234,6 @@ Module was not tested on version 2.3. ### TODO - check the Vue Storefront - Magento 2 Indexer bridge for Magento 2.3 (for Commerce and Enterprise versions). - add MSI support -- add rating review to ElasticSearch as a separate module @Agata - add a limitation of the attributes (products, categories) sent to ElasticSearch - add a limitation of the categories sent to ElasticSearch, by adding new configurations: send only categories visible in the menu, send only active categories @Agata - add a new command enabling the full indexation, which will run all the indexes necessary for VSF diff --git a/composer.json b/composer.json index 9993305d..3ea5a4d8 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "magento/module-backend": ">=100.2.0", "magento/module-catalog": ">=102.0.0", "magento/magento-composer-installer": "*", - "elasticsearch/elasticsearch": "~5.1" + "elasticsearch/elasticsearch": "~5.1|~6.1" }, "replace": { "divante/module-vsf-indexer-core": "self.version", diff --git a/docs/images/config-catalog.png b/docs/images/config-catalog.png index 510f6c3d..9638ac4d 100644 Binary files a/docs/images/config-catalog.png and b/docs/images/config-catalog.png differ diff --git a/docs/images/config-general-enable.png b/docs/images/config-general-enable.png new file mode 100644 index 00000000..7c989c9a Binary files /dev/null and b/docs/images/config-general-enable.png differ diff --git a/docs/images/config-indices.png b/docs/images/config-indices.png index 7c605966..8b992df5 100644 Binary files a/docs/images/config-indices.png and b/docs/images/config-indices.png differ diff --git a/src/module-vsbridge-indexer-catalog/Api/ApplyCategorySlugInterface.php b/src/module-vsbridge-indexer-catalog/Api/ApplyCategorySlugInterface.php new file mode 100644 index 00000000..3ff197f5 --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Api/ApplyCategorySlugInterface.php @@ -0,0 +1,22 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Api; + +/** + * Interface ApplyCategorySlugInterface + */ +interface ApplyCategorySlugInterface +{ + /** + * @param array $category + * + * @return array + */ + public function execute(array $category); +} diff --git a/src/module-vsbridge-indexer-catalog/Api/Data/CatalogConfigurationInterface.php b/src/module-vsbridge-indexer-catalog/Api/Data/CatalogConfigurationInterface.php new file mode 100644 index 00000000..84bf6ee6 --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Api/Data/CatalogConfigurationInterface.php @@ -0,0 +1,53 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Api\Data; + +/** + * Interface CatalogConfigurationInterface + */ +interface CatalogConfigurationInterface +{ + const CATALOG_SETTINGS_XML_PREFIX = 'vsbridge_indexer_settings/catalog_settings'; + + /** + * @return bool + */ + public function useMagentoUrlKeys(); + + /** + * @return bool + */ + public function useUrlKeyToGenerateSlug(); + + /** + * @return bool + */ + public function syncTierPrices(); + + /** + * @param int $storeId + * + * @return array + */ + public function getAllowedProductTypes($storeId); + + /** + * + * @return array + * @throws \Exception + */ + public function getAttributesUsedForSortBy(); + + /** + * @param int $storeId + * + * @return string + */ + public function getProductListDefaultSortBy($storeId); +} diff --git a/src/module-vsbridge-indexer-catalog/Api/LoadInventoryInterface.php b/src/module-vsbridge-indexer-catalog/Api/LoadInventoryInterface.php new file mode 100644 index 00000000..2649b470 --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Api/LoadInventoryInterface.php @@ -0,0 +1,19 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Api; + +/** + * Interface SlugGeneratorInterface + */ +interface SlugGeneratorInterface +{ + + /** + * @param string $text + * @param int $id + * + * @return string + */ + public function generate($text, $id); +} diff --git a/src/module-vsbridge-indexer-catalog/Console/Command/IndexerReindexCommand.php b/src/module-vsbridge-indexer-catalog/Console/Command/IndexerReindexCommand.php new file mode 100644 index 00000000..8c87ab57 --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Console/Command/IndexerReindexCommand.php @@ -0,0 +1,29 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Console\Command; + +use Divante\VsbridgeIndexerCatalog\Model\Indexer\ProductCategoryProcessor; +use Symfony\Component\Console\Input\InputInterface; + +/** + * Class IndexerReindexCommand + */ +class IndexerReindexCommand extends \Magento\Indexer\Console\Command\IndexerReindexCommand +{ + /** + * @inheritdoc + */ + protected function getIndexers(InputInterface $input) + { + $indexers = parent::getIndexers($input); + unset($indexers[ProductCategoryProcessor::INDEXER_ID]); + + return $indexers; + } +} diff --git a/src/module-vsbridge-indexer-catalog/Index/Mapping/AbstractMapping.php b/src/module-vsbridge-indexer-catalog/Index/Mapping/AbstractMapping.php index 6baef5b6..8ccf2470 100644 --- a/src/module-vsbridge-indexer-catalog/Index/Mapping/AbstractMapping.php +++ b/src/module-vsbridge-indexer-catalog/Index/Mapping/AbstractMapping.php @@ -184,11 +184,11 @@ private function isBooleanType(Attribute $attribute) { $attributeCode = $attribute->getAttributeCode(); - if (strstr($attributeCode, 'is_')) { + if (substr($attributeCode, 0, 3) === 'is_') { return true; } - - if ($attribute->getSourceModel() == 'eav/entity_attribute_source_boolean') { + + if ($attribute->getSourceModel() == \Magento\Catalog\Model\Product\Attribute\Source\Boolean::class) { return true; } diff --git a/src/module-vsbridge-indexer-catalog/Index/Mapping/Attribute.php b/src/module-vsbridge-indexer-catalog/Index/Mapping/Attribute.php index 83e310bb..2f8cb1c9 100644 --- a/src/module-vsbridge-indexer-catalog/Index/Mapping/Attribute.php +++ b/src/module-vsbridge-indexer-catalog/Index/Mapping/Attribute.php @@ -17,27 +17,6 @@ class Attribute implements MappingInterface */ private $eventManager; - /** - * @var string - */ - private $type; - - /** - * @inheritdoc - */ - public function setType(string $type) - { - $this->type = $type; - } - - /** - * @return string - */ - public function getType() - { - return $this->type; - } - /** * Attribute constructor. * diff --git a/src/module-vsbridge-indexer-catalog/Index/Mapping/Category.php b/src/module-vsbridge-indexer-catalog/Index/Mapping/Category.php index a967607a..a028495d 100644 --- a/src/module-vsbridge-indexer-catalog/Index/Mapping/Category.php +++ b/src/module-vsbridge-indexer-catalog/Index/Mapping/Category.php @@ -44,11 +44,6 @@ class Category extends AbstractMapping implements MappingInterface */ private $resourceModel; - /** - * @var string - */ - private $type; - /** * @var array */ @@ -166,20 +161,4 @@ public function getAttributes() { return $this->resourceModel->initAttributes(); } - - /** - * @inheritdoc - */ - public function setType(string $type) - { - $this->type = $type; - } - - /** - * @inheritdoc - */ - public function getType() - { - return $this->type; - } } diff --git a/src/module-vsbridge-indexer-catalog/Index/Mapping/Product.php b/src/module-vsbridge-indexer-catalog/Index/Mapping/Product.php index e16abdfd..7de16585 100644 --- a/src/module-vsbridge-indexer-catalog/Index/Mapping/Product.php +++ b/src/module-vsbridge-indexer-catalog/Index/Mapping/Product.php @@ -25,11 +25,6 @@ class Product extends AbstractMapping implements MappingInterface */ private $generalMapping; - /** - * @var string - */ - private $type; - /** * @var array */ @@ -170,96 +165,73 @@ private function getCommonMappingProperties() /** * @return array */ - private function getCustomProperties() + private function getCustomProperties(): array { return [ 'attribute_set_id' => ['type' => FieldInterface::TYPE_LONG], - 'bundle_options' => [ - 'properties' => [ - 'option_id' => ['type' => FieldInterface::TYPE_LONG], - 'position' => ['type' => FieldInterface::TYPE_LONG], - 'title' => ['type' => FieldInterface::TYPE_TEXT], - 'sku' => ['type' => FieldInterface::TYPE_KEYWORD], - 'product_links' => [ - 'properties' => [ - 'id' => ['type' => FieldInterface::TYPE_LONG], - 'is_default' => ['type' => FieldInterface::TYPE_BOOLEAN], - 'qty' => ['type' => FieldInterface::TYPE_DOUBLE], - 'can_change_quantity' => ['type' => FieldInterface::TYPE_BOOLEAN], - 'price' => ['type' => FieldInterface::TYPE_DOUBLE], - 'price_type' => ['type' => FieldInterface::TYPE_TEXT], - 'position' => ['type' => FieldInterface::TYPE_LONG], - 'sku' => ['type' => FieldInterface::TYPE_KEYWORD], - ], - ], - ], - ], - 'product_links' => [ - 'properties' => [ - 'linked_product_type' => ['type' => FieldInterface::TYPE_TEXT], - 'linked_product_sku' => ['type' => FieldInterface::TYPE_KEYWORD], - 'sku' => ['type' => FieldInterface::TYPE_KEYWORD], - 'position' => ['type' => FieldInterface::TYPE_LONG], - ], + 'bundle_options' => $this->getBundleOptionsMapping(), + 'product_links' => $this->getProductLinksMapping(), + 'configurable_options' => $this->getConfigurableOptionsMapping(), + 'category' => $this->getCategoryMapping(), + 'custom_options' => $this->getCustomOptionsMapping(), + 'tier_prices' => $this->getTierPricesMapping(), + ]; + } + + /** + * @return array + */ + private function getProductLinksMapping(): array + { + return [ + 'properties' => [ + 'linked_product_type' => ['type' => FieldInterface::TYPE_TEXT], + 'linked_product_sku' => ['type' => FieldInterface::TYPE_KEYWORD], + 'sku' => ['type' => FieldInterface::TYPE_KEYWORD], + 'position' => ['type' => FieldInterface::TYPE_LONG], ], - 'configurable_options' => [ - 'properties' => [ - 'label' => ['type' => FieldInterface::TYPE_TEXT], - 'id' => ['type' => FieldInterface::TYPE_LONG], - 'product_id' => ['type' => FieldInterface::TYPE_LONG], - 'attribute_code' => ['type' => FieldInterface::TYPE_TEXT], - 'attribute_id' => ['type' => FieldInterface::TYPE_LONG], - 'position' => ['type' => FieldInterface::TYPE_LONG], - 'values' => [ - 'properties' => [ - 'value_index' => ['type' => FieldInterface::TYPE_KEYWORD], - ], + ]; + } + + /** + * @return array + */ + private function getConfigurableOptionsMapping(): array + { + return [ + 'properties' => [ + 'label' => ['type' => FieldInterface::TYPE_TEXT], + 'id' => ['type' => FieldInterface::TYPE_LONG], + 'product_id' => ['type' => FieldInterface::TYPE_LONG], + 'attribute_code' => ['type' => FieldInterface::TYPE_TEXT], + 'attribute_id' => ['type' => FieldInterface::TYPE_LONG], + 'position' => ['type' => FieldInterface::TYPE_LONG], + 'values' => [ + 'properties' => [ + 'value_index' => ['type' => FieldInterface::TYPE_KEYWORD], ], ], ], - 'category' => [ - 'type' => 'nested', - 'properties' => [ - 'category_id' => ['type' => FieldInterface::TYPE_LONG], - 'position' => ['type' => FieldInterface::TYPE_LONG], - 'name' => ['type' => FieldInterface::TYPE_TEXT], - ], - ], - 'custom_options' => [ - 'properties' => [ - 'image_size_x' => ['type' => FieldInterface::TYPE_TEXT], - 'image_size_y' => ['type' => FieldInterface::TYPE_TEXT], - 'file_extension' => ['type' => FieldInterface::TYPE_TEXT], - 'is_require' => ['type' => FieldInterface::TYPE_BOOLEAN], - 'max_characters' => ['type' => FieldInterface::TYPE_TEXT], - 'option_id' => ['type' => FieldInterface::TYPE_LONG], - 'price' => ['type' => FieldInterface::TYPE_DOUBLE], - 'price_type' => ['type' => FieldInterface::TYPE_TEXT], - 'sku' => ['type' => FieldInterface::TYPE_KEYWORD], - 'sort_order' => ['type' => FieldInterface::TYPE_LONG], - 'title' => ['type' => FieldInterface::TYPE_TEXT], - 'type' => ['type' => FieldInterface::TYPE_TEXT], - 'values' => [ - 'properties' => [ - 'sku' => ['type' => FieldInterface::TYPE_KEYWORD], - 'price' => ['type' => FieldInterface::TYPE_DOUBLE], - 'title' => ['type' => FieldInterface::TYPE_TEXT], - 'price_type' => ['type' => FieldInterface::TYPE_TEXT], - 'sort_order' => ['type' => FieldInterface::TYPE_LONG], - 'option_type_id' => ['type' => FieldInterface::TYPE_INTEGER], + ]; + } + + /** + * @return array + */ + private function getCategoryMapping(): array + { + return [ + 'type' => 'nested', + 'properties' => [ + 'category_id' => ['type' => FieldInterface::TYPE_LONG], + 'position' => ['type' => FieldInterface::TYPE_LONG], + 'name' => [ + 'type' => FieldInterface::TYPE_TEXT, + 'fields' => [ + 'keyword' => [ + 'type' => FieldInterface::TYPE_KEYWORD, + 'ignore_above' => 256, ] - ] - ] - ], - 'tier_prices' => [ - 'properties' => [ - 'customer_group_d' => ['type' => FieldInterface::TYPE_INTEGER], - 'qty' => ['type' => FieldInterface::TYPE_DOUBLE], - 'value' => ['type' => FieldInterface::TYPE_DOUBLE], - 'extension_attributes' => [ - 'properties' => [ - 'website_id' => ['type' => FieldInterface::TYPE_INTEGER] - ], ], ], ], @@ -267,19 +239,82 @@ private function getCustomProperties() } /** - * @inheritdoc + * @return array + */ + private function getBundleOptionsMapping(): array + { + return [ + 'properties' => [ + 'option_id' => ['type' => FieldInterface::TYPE_LONG], + 'position' => ['type' => FieldInterface::TYPE_LONG], + 'title' => ['type' => FieldInterface::TYPE_TEXT], + 'sku' => ['type' => FieldInterface::TYPE_KEYWORD], + 'product_links' => [ + 'properties' => [ + 'id' => ['type' => FieldInterface::TYPE_LONG], + 'is_default' => ['type' => FieldInterface::TYPE_BOOLEAN], + 'qty' => ['type' => FieldInterface::TYPE_DOUBLE], + 'can_change_quantity' => ['type' => FieldInterface::TYPE_BOOLEAN], + 'price' => ['type' => FieldInterface::TYPE_DOUBLE], + 'price_type' => ['type' => FieldInterface::TYPE_TEXT], + 'position' => ['type' => FieldInterface::TYPE_LONG], + 'sku' => ['type' => FieldInterface::TYPE_KEYWORD], + ], + ], + ] + ]; + } + + /** + * @return array */ - public function setType(string $type) + private function getCustomOptionsMapping(): array { - $this->type = $type; + return [ + 'properties' => [ + 'image_size_x' => ['type' => FieldInterface::TYPE_TEXT], + 'image_size_y' => ['type' => FieldInterface::TYPE_TEXT], + 'file_extension' => ['type' => FieldInterface::TYPE_TEXT], + 'is_require' => ['type' => FieldInterface::TYPE_BOOLEAN], + 'max_characters' => ['type' => FieldInterface::TYPE_TEXT], + 'option_id' => ['type' => FieldInterface::TYPE_LONG], + 'price' => ['type' => FieldInterface::TYPE_DOUBLE], + 'price_type' => ['type' => FieldInterface::TYPE_TEXT], + 'sku' => ['type' => FieldInterface::TYPE_KEYWORD], + 'sort_order' => ['type' => FieldInterface::TYPE_LONG], + 'title' => ['type' => FieldInterface::TYPE_TEXT], + 'type' => ['type' => FieldInterface::TYPE_TEXT], + 'values' => [ + 'properties' => [ + 'sku' => ['type' => FieldInterface::TYPE_KEYWORD], + 'price' => ['type' => FieldInterface::TYPE_DOUBLE], + 'title' => ['type' => FieldInterface::TYPE_TEXT], + 'price_type' => ['type' => FieldInterface::TYPE_TEXT], + 'sort_order' => ['type' => FieldInterface::TYPE_LONG], + 'option_type_id' => ['type' => FieldInterface::TYPE_INTEGER], + ] + ] + ] + ]; } /** - * @return string + * @return array */ - public function getType() + private function getTierPricesMapping(): array { - return $this->type; + return [ + 'properties' => [ + 'customer_group_d' => ['type' => FieldInterface::TYPE_INTEGER], + 'qty' => ['type' => FieldInterface::TYPE_DOUBLE], + 'value' => ['type' => FieldInterface::TYPE_DOUBLE], + 'extension_attributes' => [ + 'properties' => [ + 'website_id' => ['type' => FieldInterface::TYPE_INTEGER] + ], + ], + ], + ]; } /** diff --git a/src/module-vsbridge-indexer-catalog/Model/Attribute/LoadOptionLabelById.php b/src/module-vsbridge-indexer-catalog/Model/Attribute/LoadOptionLabelById.php new file mode 100644 index 00000000..1787f4d1 --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Model/Attribute/LoadOptionLabelById.php @@ -0,0 +1,79 @@ + + * @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/Category/ApplyCategorySlug.php b/src/module-vsbridge-indexer-catalog/Model/Category/ApplyCategorySlug.php new file mode 100644 index 00000000..4567e0b0 --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Model/Category/ApplyCategorySlug.php @@ -0,0 +1,74 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Model\Category; + +use Divante\VsbridgeIndexerCatalog\Api\ApplyCategorySlugInterface; +use Divante\VsbridgeIndexerCatalog\Api\Data\CatalogConfigurationInterface; +use Divante\VsbridgeIndexerCatalog\Api\SlugGeneratorInterface; + +/** + * Class ApplyCategorySlug + */ +class ApplyCategorySlug implements ApplyCategorySlugInterface +{ + + /** + * @var CatalogConfigurationInterface + */ + private $settings; + + /** + * @var SlugGeneratorInterface + */ + private $slugGenerator; + + /** + * ApplySlug constructor. + * + * @param SlugGeneratorInterface $slugGenerator + * @param CatalogConfigurationInterface $configSettings + */ + public function __construct( + SlugGeneratorInterface $slugGenerator, + CatalogConfigurationInterface $configSettings + ) { + $this->settings = $configSettings; + $this->slugGenerator = $slugGenerator; + } + + /** + * @inheritdoc + */ + public function execute(array $category) + { + if ($this->settings->useMagentoUrlKeys()) { + if (!isset($category['url_key'])) { + $slug = $this->slugGenerator->generate( + $category['name'], + $category['entity_id'] + ); + $category['url_key'] = $slug; + } + + $category['slug'] = $category['url_key']; + } else { + $text = $category['name']; + + if ($this->settings->useUrlKeyToGenerateSlug() && isset($category['url_key'])) { + $text = $category['url_key']; + } + + $slug = $this->slugGenerator->generate($text, $category['entity_id']); + $category['url_key'] = $slug; + $category['slug'] = $slug; + } + + return $category; + } +} diff --git a/src/module-vsbridge-indexer-catalog/Model/GalleryProcessor.php b/src/module-vsbridge-indexer-catalog/Model/GalleryProcessor.php index 6e89900a..522f0566 100644 --- a/src/module-vsbridge-indexer-catalog/Model/GalleryProcessor.php +++ b/src/module-vsbridge-indexer-catalog/Model/GalleryProcessor.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-catalog/Model/Indexer/Action/Attribute.php b/src/module-vsbridge-indexer-catalog/Model/Indexer/Action/Attribute.php index d8613fee..ebb57b12 100644 --- a/src/module-vsbridge-indexer-catalog/Model/Indexer/Action/Attribute.php +++ b/src/module-vsbridge-indexer-catalog/Model/Indexer/Action/Attribute.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-catalog/Model/Indexer/Action/Category.php b/src/module-vsbridge-indexer-catalog/Model/Indexer/Action/Category.php index 70ec1296..813da968 100644 --- a/src/module-vsbridge-indexer-catalog/Model/Indexer/Action/Category.php +++ b/src/module-vsbridge-indexer-catalog/Model/Indexer/Action/Category.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-catalog/Model/Indexer/Action/Product.php b/src/module-vsbridge-indexer-catalog/Model/Indexer/Action/Product.php index a91ebf4c..b1b9326c 100644 --- a/src/module-vsbridge-indexer-catalog/Model/Indexer/Action/Product.php +++ b/src/module-vsbridge-indexer-catalog/Model/Indexer/Action/Product.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-catalog/Model/Indexer/Attribute.php b/src/module-vsbridge-indexer-catalog/Model/Indexer/Attribute.php index 752e8f1e..fd462a48 100644 --- a/src/module-vsbridge-indexer-catalog/Model/Indexer/Attribute.php +++ b/src/module-vsbridge-indexer-catalog/Model/Indexer/Attribute.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-catalog/Model/Indexer/Category.php b/src/module-vsbridge-indexer-catalog/Model/Indexer/Category.php index d0f833b8..59cc2653 100644 --- a/src/module-vsbridge-indexer-catalog/Model/Indexer/Category.php +++ b/src/module-vsbridge-indexer-catalog/Model/Indexer/Category.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Category/AttributeData.php b/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Category/AttributeData.php index a71abb51..c6cad721 100644 --- a/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Category/AttributeData.php +++ b/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Category/AttributeData.php @@ -1,12 +1,18 @@ + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ namespace Divante\VsbridgeIndexerCatalog\Model\Indexer\DataProvider\Category; use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Category\Children as CategoryChildrenResource; use Divante\VsbridgeIndexerCore\Indexer\DataFilter; use Divante\VsbridgeIndexerCatalog\Model\Attributes\CategoryChildAttributes; -use Divante\VsbridgeIndexerCatalog\Model\ConfigSettings; -use Divante\VsbridgeIndexerCatalog\Model\SlugGenerator; +use Divante\VsbridgeIndexerCatalog\Api\Data\CatalogConfigurationInterface; +use Divante\VsbridgeIndexerCatalog\Api\ApplyCategorySlugInterface; use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Category\AttributeDataProvider; use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Category\ProductCount as ProductCountResourceModel; @@ -69,22 +75,23 @@ class AttributeData private $childrenProductCount = []; /** - * @var ConfigSettings + * @var CatalogConfigurationInterface */ private $settings; /** - * @var SlugGenerator + * @var ApplyCategorySlugInterface */ - private $slugGenerator; + private $applyCategorySlug; /** * AttributeData constructor. * * @param AttributeDataProvider $attributeResource * @param CategoryChildrenResource $childrenResource - * @param SlugGenerator\Proxy $catalogHelper - * @param ConfigSettings $configSettings + * @param ProductCountResourceModel $productCountResource + * @param ApplyCategorySlugInterface $applyCategorySlug + * @param CatalogConfigurationInterface $configSettings * @param CategoryChildAttributes $categoryChildAttributes * @param DataFilter $dataFilter */ @@ -92,13 +99,13 @@ public function __construct( AttributeDataProvider $attributeResource, CategoryChildrenResource $childrenResource, ProductCountResourceModel $productCountResource, - SlugGenerator\Proxy $catalogHelper, - ConfigSettings $configSettings, + ApplyCategorySlugInterface $applyCategorySlug, + CatalogConfigurationInterface $configSettings, CategoryChildAttributes $categoryChildAttributes, DataFilter $dataFilter ) { $this->settings = $configSettings; - $this->slugGenerator = $catalogHelper; + $this->applyCategorySlug = $applyCategorySlug; $this->productCountResource = $productCountResource; $this->attributeResourceModel = $attributeResource; $this->childrenResourceModel = $childrenResource; @@ -114,7 +121,7 @@ public function __construct( */ public function addData(array $indexData, $storeId) { - $this->settings->getAttributesUsedForSortBy($storeId); + $this->settings->getAttributesUsedForSortBy(); /** * TODO add option to load only specific categories */ @@ -267,23 +274,7 @@ private function addSortOptions(array $category, $storeId) */ private function addSlug(array $categoryDTO) { - if ($this->settings->useMagentoUrlKeys()) { - if (!isset($categoryDTO['url_key'])) { - $slug = $this->slugGenerator->generate( - $categoryDTO['name'], - $categoryDTO['entity_id'] - ); - $categoryDTO['url_key'] = $slug; - } - - $categoryDTO['slug'] = $categoryDTO['url_key']; - } else { - $slug = $this->slugGenerator->generate($categoryDTO['name'], $categoryDTO['entity_id']); - $categoryDTO['url_key'] = $slug; - $categoryDTO['slug'] = $slug; - } - - return $categoryDTO; + return $this->applyCategorySlug->execute($categoryDTO); } /** diff --git a/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/AttributeData.php b/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/AttributeData.php index 3a59a905..fd84a7d0 100644 --- a/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/AttributeData.php +++ b/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/AttributeData.php @@ -1,9 +1,9 @@ + * @package Divante\VsbridgeIndexerCatalog + * @author Agata Firlejczyk * @copyright 2019 Divante Sp. z o.o. - * @license See LICENSE_DIVANTE.txt for license details. + * @license See LICENSE_DIVANTE.txt for license details. */ namespace Divante\VsbridgeIndexerCatalog\Model\Indexer\DataProvider\Product; @@ -11,8 +11,8 @@ use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\AttributeDataProvider; use Divante\VsbridgeIndexerCore\Api\DataProviderInterface; use Divante\VsbridgeIndexerCore\Indexer\DataFilter; -use Divante\VsbridgeIndexerCatalog\Model\ConfigSettings; -use Divante\VsbridgeIndexerCatalog\Model\SlugGenerator; +use Divante\VsbridgeIndexerCatalog\Api\Data\CatalogConfigurationInterface; +use Divante\VsbridgeIndexerCatalog\Api\SlugGeneratorInterface; use Divante\VsbridgeIndexerCatalog\Model\ProductUrlPathGenerator; /** @@ -31,12 +31,12 @@ class AttributeData implements DataProviderInterface private $dataFilter; /** - * @var ConfigSettings + * @var CatalogConfigurationInterface */ private $settings; /** - * @var SlugGenerator + * @var SlugGeneratorInterface */ private $slugGenerator; @@ -48,15 +48,15 @@ class AttributeData implements DataProviderInterface /** * AttributeData constructor. * - * @param ConfigSettings $configSettings - * @param SlugGenerator\Proxy $slugGenerator + * @param CatalogConfigurationInterface $configSettings + * @param SlugGeneratorInterface $slugGenerator * @param ProductUrlPathGenerator $productUrlPathGenerator * @param DataFilter $dataFilter * @param AttributeDataProvider $resourceModel */ public function __construct( - ConfigSettings $configSettings, - SlugGenerator\Proxy $slugGenerator, + CatalogConfigurationInterface $configSettings, + SlugGeneratorInterface $slugGenerator, ProductUrlPathGenerator $productUrlPathGenerator, DataFilter $dataFilter, AttributeDataProvider $resourceModel @@ -81,14 +81,7 @@ public function addData(array $indexData, $storeId) foreach ($attributes as $entityId => $attributesData) { $productData = array_merge($indexData[$entityId], $attributesData); - - if ($this->settings->useMagentoUrlKeys()) { - $productData['slug'] = $productData['url_key']; - } else { - $slug = $this->slugGenerator->generate($productData['name'], $entityId); - $productData['slug'] = $slug; - } - + $productData = $this->applySlug($productData); $indexData[$entityId] = $productData; } @@ -97,4 +90,30 @@ public function addData(array $indexData, $storeId) return $indexData; } + + /** + * @param array $productData + * + * @return array + */ + private function applySlug(array $productData): array + { + $entityId = $productData['id']; + + if ($this->settings->useMagentoUrlKeys()) { + $productData['slug'] = $productData['url_key']; + } else { + $text = $productData['name']; + + if ($this->settings->useUrlKeyToGenerateSlug()) { + $text = $productData['url_key']; + } + + $slug = $this->slugGenerator->generate($text, $entityId); + $productData['slug'] = $slug; + $productData['url_key'] = $slug; + } + + return $productData; + } } 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 55fa1128..ceefbb4b 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,11 +8,12 @@ namespace Divante\VsbridgeIndexerCatalog\Model\Indexer\DataProvider\Product; +use Divante\VsbridgeIndexerCatalog\Model\Attribute\LoadOptionLabelById; use Divante\VsbridgeIndexerCatalog\Model\Attributes\ConfigurableAttributes; use Divante\VsbridgeIndexerCatalog\Model\InventoryProcessor; use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\AttributeDataProvider; use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\Configurable as ConfigurableResource; -use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\Inventory as InventoryResource; +use Divante\VsbridgeIndexerCatalog\Api\LoadInventoryInterface; use Divante\VsbridgeIndexerCatalog\Model\TierPriceProcessor; use Divante\VsbridgeIndexerCore\Api\DataProviderInterface; use Divante\VsbridgeIndexerCore\Indexer\DataFilter; @@ -55,9 +56,9 @@ class ConfigurableData implements DataProviderInterface private $resourceAttributeModel; /** - * @var InventoryResource + * @var LoadInventoryInterface */ - private $inventoryResource; + private $loadInventory; /** * @var InventoryProcessor @@ -74,13 +75,19 @@ class ConfigurableData implements DataProviderInterface */ private $tierPriceProcessor; + /** + * @var LoadOptionLabelById + */ + private $loadOptionLabelById; + /** * ConfigurableData constructor. * * @param DataFilter $dataFilter * @param ConfigurableResource $configurableResource * @param AttributeDataProvider $attributeResource - * @param InventoryResource $inventoryResource + * @param LoadInventoryInterface $loadInventory + * @param LoadOptionLabelById $loadOptionLabelById * @param ConfigurableAttributes $configurableAttributes * @param TierPriceProcessor $tierPriceProcessor * @param InventoryProcessor $inventoryProcessor @@ -89,7 +96,8 @@ public function __construct( DataFilter $dataFilter, ConfigurableResource $configurableResource, AttributeDataProvider $attributeResource, - InventoryResource $inventoryResource, + LoadInventoryInterface $loadInventory, + LoadOptionLabelById $loadOptionLabelById, ConfigurableAttributes $configurableAttributes, TierPriceProcessor $tierPriceProcessor, InventoryProcessor $inventoryProcessor @@ -97,9 +105,10 @@ public function __construct( $this->dataFilter = $dataFilter; $this->configurableResource = $configurableResource; $this->resourceAttributeModel = $attributeResource; - $this->inventoryResource = $inventoryResource; + $this->loadInventory = $loadInventory; $this->inventoryProcessor = $inventoryProcessor; $this->tierPriceProcessor = $tierPriceProcessor; + $this->loadOptionLabelById = $loadOptionLabelById; $this->configurableAttributes = $configurableAttributes; } @@ -118,7 +127,7 @@ public function addData(array $indexData, $storeId) continue; } - $productDTO = $this->applyConfigurableOptions($productDTO); + $productDTO = $this->applyConfigurableOptions($productDTO, $storeId); $indexData[$productId] = $this->prepareConfigurableProduct($productDTO); } @@ -142,9 +151,7 @@ private function prepareConfigurableChildrenAttributes(array $indexData, $storeI return $indexData; } - $childIds = array_keys($allChildren); - - $stockRowData = $this->inventoryResource->loadChildrenData($storeId, $childIds); + $stockRowData = $this->loadInventory->execute($allChildren, $storeId); $configurableAttributeCodes = $this->configurableResource->getConfigurableAttributeCodes(); $requiredAttributes = array_merge( @@ -200,11 +207,12 @@ private function getRequiredChildrenAttributes() /** * Apply attributes to product variants + extra options for products necessary for vsf * @param array $productDTO + * @param $storeId * * @return array * @throws \Exception */ - private function applyConfigurableOptions(array $productDTO) + private function applyConfigurableOptions(array $productDTO, $storeId) { $configurableChildren = $productDTO['configurable_children']; $productAttributeOptions = @@ -233,7 +241,11 @@ private function applyConfigurableOptions(array $productDTO) $values = array_values(array_unique($values)); foreach ($values as $value) { - $productAttribute['values'][] = ['value_index' => $value]; + $label = $this->loadOptionLabelById->execute($attributeCode, $value, $storeId); + $productAttribute['values'][] = [ + 'value_index' => $value, + 'label' => $label, + ]; } $productDTO['configurable_options'][] = $productAttribute; @@ -287,12 +299,19 @@ private function prepareConfigurableProduct(array $productDTO) */ private function hasPrice(array $product) { - if (!isset($product['price'])) { - return false; - } + $priceFields = [ + 'price', + 'final_price', + ]; + + foreach ($priceFields as $field) { + if (!isset($product[$field])) { + return false; + } - if (0 === (int)$product['price']) { - return false; + if (0 === (int)$product[$field]) { + return false; + } } return true; diff --git a/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/Inventory.php b/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/Inventory.php index 3c308595..71960000 100644 --- a/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/Inventory.php +++ b/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/Inventory.php @@ -8,9 +8,9 @@ namespace Divante\VsbridgeIndexerCatalog\Model\Indexer\DataProvider\Product; -use Divante\VsbridgeIndexerCore\Api\DataProviderInterface; +use Divante\VsbridgeIndexerCatalog\Api\LoadInventoryInterface; use Divante\VsbridgeIndexerCatalog\Model\InventoryProcessor; -use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\Inventory as Resource; +use Divante\VsbridgeIndexerCore\Api\DataProviderInterface; /** * Class Inventory @@ -19,9 +19,9 @@ class Inventory implements DataProviderInterface { /** - * @var \Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\Inventory + * @var LoadInventoryInterface */ - private $resourceModel; + private $getInventory; /** * @var InventoryProcessor @@ -31,14 +31,14 @@ class Inventory implements DataProviderInterface /** * Inventory constructor. * - * @param Resource $resource + * @param LoadInventoryInterface $getInventory * @param InventoryProcessor $inventoryProcessor */ public function __construct( - Resource $resource, + LoadInventoryInterface $getInventory, InventoryProcessor $inventoryProcessor ) { - $this->resourceModel = $resource; + $this->getInventory = $getInventory; $this->inventoryProcessor = $inventoryProcessor; } @@ -50,10 +50,11 @@ public function __construct( */ public function addData(array $indexData, $storeId) { - $inventoryData = $this->resourceModel->loadInventoryData($storeId, array_keys($indexData)); + $inventoryData = $this->getInventory->execute($indexData, $storeId); foreach ($inventoryData as $inventoryDataRow) { $productId = (int) $inventoryDataRow['product_id']; + unset($inventoryDataRow['product_id']); $indexData[$productId]['stock'] = $this->inventoryProcessor->prepareInventoryData($storeId, $inventoryDataRow); } diff --git a/src/module-vsbridge-indexer-catalog/Model/Indexer/Product.php b/src/module-vsbridge-indexer-catalog/Model/Indexer/Product.php index 857059a1..6fd3a418 100644 --- a/src/module-vsbridge-indexer-catalog/Model/Indexer/Product.php +++ b/src/module-vsbridge-indexer-catalog/Model/Indexer/Product.php @@ -1,9 +1,9 @@ + * @package Divante\VsbridgeIndexerCatalog + * @author Agata Firlejczyk * @copyright 2019 Divante Sp. z o.o. - * @license See LICENSE_DIVANTE.txt for license details. + * @license See LICENSE_DIVANTE.txt for license details. */ namespace Divante\VsbridgeIndexerCatalog\Model\Indexer; diff --git a/src/module-vsbridge-indexer-catalog/Model/Indexer/ProductCategory.php b/src/module-vsbridge-indexer-catalog/Model/Indexer/ProductCategory.php new file mode 100644 index 00000000..038c1c61 --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Model/Indexer/ProductCategory.php @@ -0,0 +1,115 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Model\Indexer; + +use Divante\VsbridgeIndexerCatalog\Model\Indexer\Action\Product as ProductAction; +use Divante\VsbridgeIndexerCore\Indexer\GenericIndexerHandler; +use Divante\VsbridgeIndexerCore\Indexer\StoreManager; +use Divante\VsbridgeIndexerCore\Cache\Processor as CacheProcessor; + +/** + * Class ProductCategory + */ +class ProductCategory implements \Magento\Framework\Indexer\ActionInterface, \Magento\Framework\Mview\ActionInterface +{ + /** + * @var GenericIndexerHandler + */ + private $indexHandler; + + /** + * @var ProductAction + */ + private $productAction; + + /** + * @var StoreManager + */ + private $storeManager; + + /** + * @var CacheProcessor + */ + private $cacheProcessor; + + /** + * Category constructor. + * + * @param CacheProcessor $cacheProcessor + * @param GenericIndexerHandler $indexerHandler + * @param StoreManager $storeManager + * @param ProductAction $action + */ + public function __construct( + CacheProcessor $cacheProcessor, + GenericIndexerHandler $indexerHandler, + StoreManager $storeManager, + ProductAction $action + ) { + $this->productAction = $action; + $this->storeManager = $storeManager; + $this->indexHandler = $indexerHandler; + $this->cacheProcessor = $cacheProcessor; + } + + /** + * @inheritdoc + */ + public function execute($ids) + { + $stores = $this->storeManager->getStores(); + + foreach ($stores as $store) { + $this->rebuild($store, $ids); + } + } + + /** + * @inheritdoc + */ + public function executeFull() + { + $stores = $this->storeManager->getStores(); + + foreach ($stores as $store) { + $this->rebuild($store); + } + } + + /** + * @param \Magento\Store\Api\Data\StoreInterface $store + * @param array $productIds + * + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function rebuild($store, array $productIds = []) + { + $this->indexHandler->updateIndex( + $this->productAction->rebuild($store->getId(), $productIds), + $store, + ['category_data'] + ); + } + + /** + * @inheritdoc + */ + public function executeList(array $ids) + { + $this->execute($ids); + } + + /** + * @inheritdoc + */ + public function executeRow($id) + { + $this->execute([$id]); + } +} diff --git a/src/module-vsbridge-indexer-catalog/Model/Indexer/ProductCategoryProcessor.php b/src/module-vsbridge-indexer-catalog/Model/Indexer/ProductCategoryProcessor.php new file mode 100644 index 00000000..b53e31ef --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Model/Indexer/ProductCategoryProcessor.php @@ -0,0 +1,30 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Model\Indexer; + +/** + * Class ProductCategoryProcessor + */ +class ProductCategoryProcessor extends \Magento\Framework\Indexer\AbstractProcessor +{ + /** + * Indexer ID + */ + const INDEXER_ID = 'vsbridge_product_category'; + + /** + * Mark Vsbridge Product indexer as invalid + * + * @return void + */ + public function markIndexerAsInvalid() + { + $this->getIndexer()->invalidate(); + } +} diff --git a/src/module-vsbridge-indexer-catalog/Model/Inventory/Fields.php b/src/module-vsbridge-indexer-catalog/Model/Inventory/Fields.php new file mode 100644 index 00000000..4262c14e --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Model/Inventory/Fields.php @@ -0,0 +1,74 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +declare(strict_types=1); + +namespace Divante\VsbridgeIndexerCatalog\Model\Inventory; + +/** + * Class Fields + */ +class Fields +{ + /** + * @var array + */ + private $childRequiredFields = [ + 'product_id', + 'is_in_stock', + 'min_qty', + 'notify_stock_qty', + 'use_config_notify_stock_qty', + 'qty', + ]; + + /** + * @var array + */ + private $fields = [ + 'product_id', + 'item_id', + 'stock_id', + 'qty', + 'is_in_stock', + 'is_qty_decimal', + 'use_config_min_qty', + 'min_qty', + 'use_config_min_sale_qty', + 'min_sale_qty', + 'use_config_max_sale_qty', + 'max_sale_qty', + 'use_config_notify_stock_qty', + 'notify_stock_qty', + 'use_config_qty_increments', + 'backorders', + 'use_config_backorders', + 'qty_increments', + 'use_config_enable_qty_inc', + 'enable_qty_increments', + 'use_config_manage_stock', + 'manage_stock', + 'low_stock_date', + ]; + + /** + * @return array + */ + public function getRequiredColumns(): array + { + return $this->fields; + } + + /** + * @return array + */ + public function getChildRequiredColumns(): array + { + return $this->childRequiredFields; + } +} diff --git a/src/module-vsbridge-indexer-catalog/Model/LoadChildrenInventory.php b/src/module-vsbridge-indexer-catalog/Model/LoadChildrenInventory.php new file mode 100644 index 00000000..26de46a7 --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Model/LoadChildrenInventory.php @@ -0,0 +1,45 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +declare(strict_types=1); + +namespace Divante\VsbridgeIndexerCatalog\Model; + +use Divante\VsbridgeIndexerCatalog\Api\LoadInventoryInterface; +use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\Inventory as InventoryResource; + +/** + * Class LoadChildrenInventory + */ +class LoadChildrenInventory implements LoadInventoryInterface +{ + /** + * @var InventoryResource + */ + private $resource; + + /** + * LoadChildrenInventory constructor. + * + * @param InvetoryResource $resource + */ + public function __construct(InventoryResource $resource) + { + $this->resource = $resource; + } + + /** + * @inheritdoc + */ + public function execute(array $indexData, int $storeId): array + { + $productIds = array_keys($indexData); + + return $this->resource->loadChildrenInventory($productIds); + } +} diff --git a/src/module-vsbridge-indexer-catalog/Model/LoadInventory.php b/src/module-vsbridge-indexer-catalog/Model/LoadInventory.php new file mode 100644 index 00000000..1e72792c --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Model/LoadInventory.php @@ -0,0 +1,45 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +declare(strict_types=1); + +namespace Divante\VsbridgeIndexerCatalog\Model; + +use Divante\VsbridgeIndexerCatalog\Api\LoadInventoryInterface; +use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\Inventory as InventoryResource; + +/** + * Class LoadInventory + */ +class LoadInventory implements LoadInventoryInterface +{ + /** + * @var InventoryResource + */ + private $resource; + + /** + * LoadChildrenInventory constructor. + * + * @param InvetoryResource $resource + */ + public function __construct(InventoryResource $resource) + { + $this->resource = $resource; + } + + /** + * @inheritdoc + */ + public function execute(array $indexData, int $storeId): array + { + $productIds = array_keys($indexData); + + return $this->resource->loadInventory($productIds); + } +} diff --git a/src/module-vsbridge-indexer-catalog/Model/Product/LinkTypeMapper.php b/src/module-vsbridge-indexer-catalog/Model/Product/LinkTypeMapper.php new file mode 100644 index 00000000..68dae9bc --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Model/Product/LinkTypeMapper.php @@ -0,0 +1,49 @@ + 'related', + ProductLink::LINK_TYPE_UPSELL => 'upsell', + ProductLink::LINK_TYPE_CROSSSELL => 'crosssell', + GroupedLink::LINK_TYPE_GROUPED => 'associated', + ]; + + /** + * @param int $typeId + * + * @return string|null + */ + public function map(int $typeId) + { + $linksTypesMap = $this->getTypesMap(); + + if (isset($linksTypesMap[$typeId])) { + return $linksTypesMap[$typeId]; + } + + return null; + } + + /** + * @return array + */ + public function getTypesMap() + { + return $this->linkTypesMap; + } +} diff --git a/src/module-vsbridge-indexer-catalog/Model/ProductMetaData.php b/src/module-vsbridge-indexer-catalog/Model/ProductMetaData.php index 0546443a..ed5ea6e7 100644 --- a/src/module-vsbridge-indexer-catalog/Model/ProductMetaData.php +++ b/src/module-vsbridge-indexer-catalog/Model/ProductMetaData.php @@ -37,7 +37,6 @@ public function __construct(MetadataPool $metadataPool) /** * @return \Magento\Framework\EntityManager\EntityMetadataInterface - * @throws \Exception */ public function get() { diff --git a/src/module-vsbridge-indexer-catalog/Model/ProductUrlPathGenerator.php b/src/module-vsbridge-indexer-catalog/Model/ProductUrlPathGenerator.php index 87382e1d..0059cf03 100644 --- a/src/module-vsbridge-indexer-catalog/Model/ProductUrlPathGenerator.php +++ b/src/module-vsbridge-indexer-catalog/Model/ProductUrlPathGenerator.php @@ -46,6 +46,8 @@ public function __construct( } /** + * Add URL path + * * @param array $products * @param int $storeId * @@ -59,7 +61,10 @@ public function addUrlPath(array $products, $storeId) $rewrites = $this->rewriteResource->getRawRewritesData($productIds, $storeId); foreach ($rewrites as $productId => $rewrite) { - $rewrite = mb_substr($rewrite, 0, -strlen($urlSuffix)); + if ($urlSuffix != "") { + $rewrite = mb_substr($rewrite, 0, -strlen($urlSuffix)); + } + $products[$productId]['url_path'] = $rewrite; } @@ -74,7 +79,7 @@ public function addUrlPath(array $products, $storeId) private function getProductUrlSuffix() { if (null === $this->productUrlSuffix) { - $this->productUrlSuffix = $this->scopeConfig->getValue( + $this->productUrlSuffix = (string) $this->scopeConfig->getValue( \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator::XML_PATH_PRODUCT_URL_SUFFIX ); } diff --git a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/AbstractEavAttributes.php b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/AbstractEavAttributes.php index 792a19c9..1610f14d 100644 --- a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/AbstractEavAttributes.php +++ b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/AbstractEavAttributes.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. @@ -15,7 +15,7 @@ /** * Class EavAttributes */ -abstract class AbstractEavAttributes +abstract class AbstractEavAttributes implements EavAttributesInterface { /** * @var array @@ -152,7 +152,13 @@ private function prepareValues(array $values) $attribute = $this->attributesById[$value['attribute_id']]; if ($attribute->getFrontendInput() === 'multiselect') { - $value['value'] = explode(',', $value['value']); + $options = explode(',', $value['value']); + + if (!empty($options)) { + $options = array_map('intval', $options); + } + + $value['value'] = $options; } $attributeCode = $attribute->getAttributeCode(); diff --git a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Category.php b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Category.php index 168a4844..022e7747 100644 --- a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Category.php +++ b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Category.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Category/Children.php b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Category/Children.php index 89808e5c..d2f2586a 100644 --- a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Category/Children.php +++ b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Category/Children.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/EavAttributesInterface.php b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/EavAttributesInterface.php new file mode 100644 index 00000000..22d9adaa --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/EavAttributesInterface.php @@ -0,0 +1,26 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Model\ResourceModel; + +/** + * Interface EavAttributesInterface + */ +interface EavAttributesInterface +{ + + /** + * @param int $storeId + * @param array $entityIds + * @param array $requiredAttributes + * + * @return array + * @throws \Exception + */ + public function loadAttributesData($storeId, array $entityIds, array $requiredAttributes = null); +} diff --git a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product.php b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product.php index df4b41e0..c1721a5c 100644 --- a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product.php +++ b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. @@ -8,7 +8,7 @@ namespace Divante\VsbridgeIndexerCatalog\Model\ResourceModel; -use Divante\VsbridgeIndexerCatalog\Model\ConfigSettings; +use Divante\VsbridgeIndexerCatalog\Api\Data\CatalogConfigurationInterface; use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\AttributeDataProvider; use Magento\Catalog\Model\Product\Attribute\Source\Status; use Magento\Framework\App\ResourceConnection; @@ -44,7 +44,7 @@ class Product private $attributeDataProvider; /** - * @var ConfigSettings + * @var CatalogConfigurationInterface */ private $productSettings; @@ -61,7 +61,7 @@ class Product /** * Product constructor. * - * @param ConfigSettings $configSettings + * @param CatalogConfigurationInterface $configSettings * @param AttributeDataProvider $attributeDataProvider * @param ResourceConnection $resourceConnection * @param StoreManagerInterface $storeManager @@ -69,7 +69,7 @@ class Product * @param DbHelper $dbHelper */ public function __construct( - ConfigSettings $configSettings, + CatalogConfigurationInterface $configSettings, AttributeDataProvider $attributeDataProvider, ResourceConnection $resourceConnection, StoreManagerInterface $storeManager, diff --git a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Bundle.php b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Bundle.php index 755e61aa..aa0e11e8 100644 --- a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Bundle.php +++ b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Bundle.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Configurable.php b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Configurable.php index 9c9ef875..bf03f018 100644 --- a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Configurable.php +++ b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Configurable.php @@ -7,6 +7,7 @@ use Magento\ConfigurableProduct\Model\Product\Type\Configurable as ConfigurableType; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Helper as DbHelper; +use Divante\VsbridgeIndexerCatalog\Model\ProductMetaData; /** * Class Configurable @@ -34,6 +35,11 @@ class Configurable */ private $productResource; + /** + * @var ProductMetaData + */ + private $productMetaData; + /** * Array of the ids of configurable products from $productCollection * @@ -80,17 +86,20 @@ class Configurable * * @param AttributeDataProvider $attributeDataProvider * @param Product $productResource + * @param ProductMetaData $productMetaData * @param ResourceConnection $resourceConnection * @param DbHelper $dbHelper */ public function __construct( AttributeDataProvider $attributeDataProvider, Product $productResource, + ProductMetaData $productMetaData, ResourceConnection $resourceConnection, DbHelper $dbHelper ) { $this->attributeDataProvider = $attributeDataProvider; $this->resource = $resourceConnection; + $this->productMetaData = $productMetaData; $this->productResource = $productResource; $this->dbHelper = $dbHelper; } @@ -155,7 +164,8 @@ public function getProductConfigurableAttributes(array $product) private function getProductConfigurableAttributeIds(array $product) { $attributes = $this->getConfigurableProductAttributes(); - $productId = $product['id']; + $linkField = $this->productMetaData->get()->getLinkField(); + $productId = $product[$linkField]; if (!isset($attributes[$productId])) { throw new \Exception( @@ -175,7 +185,7 @@ private function getProductConfigurableAttributeIds(array $product) private function getConfigurableProductAttributes() { if (!$this->configurableProductAttributes) { - $productIds = $this->getConfigurableProductIds(); + $productIds = $this->getParentIds(); $attributes = $this->getConfigurableAttributesForProductsFromResource($productIds); $this->configurableProductAttributes = $attributes; } @@ -262,12 +272,17 @@ private function getConfigurableAttributeFullInfo() private function getConfigurableProductIds() { if (null === $this->configurableProductIds) { + $linkField = $this->productMetaData->get()->getLinkField(); + $entityField = $this->productMetaData->get()->getIdentifierField(); + $this->configurableProductIds = []; $products = $this->productsData; foreach ($products as $product) { if ($product['type_id'] == ConfigurableType::TYPE_CODE) { - $this->configurableProductIds[] = $product['id']; + $entityId = $product[$entityField]; + $linkId = $product[$linkField]; + $this->configurableProductIds[$linkId] = $entityId; } } } @@ -275,6 +290,16 @@ private function getConfigurableProductIds() return $this->configurableProductIds; } + /** + * @return array + */ + private function getParentIds() + { + $productIds = $this->getConfigurableProductIds(); + + return array_keys($productIds); + } + /** * Return all associated simple products for the configurable products in * the current product collection. @@ -288,13 +313,14 @@ private function getConfigurableProductIds() public function getSimpleProducts($storeId) { if (null === $this->simpleProducts) { - $parentIds = $this->getConfigurableProductIds(); + $parentIds = $this->getParentIds(); $childProduct = $this->productResource->loadChildrenProducts($parentIds, $storeId); /** @var Product $product */ foreach ($childProduct as $product) { $simpleId = $product['entity_id']; $parentIds = explode(',', $product['parent_ids']); + $parentIds = $this->mapLinkFieldToEntityId($parentIds); $product['parent_ids'] = $parentIds; $this->simpleProducts[$simpleId] = $product; } @@ -303,6 +329,22 @@ public function getSimpleProducts($storeId) return $this->simpleProducts; } + /** + * @param array $linkIds + * + * @return array + */ + private function mapLinkFieldToEntityId(array $linkIds) + { + $productIds = []; + + foreach ($linkIds as $id) { + $productIds[] = $this->configurableProductIds[$id]; + } + + return $productIds; + } + /** * @return \Magento\Framework\DB\Adapter\AdapterInterface */ diff --git a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Inventory.php b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Inventory.php index b15bd1c2..471620c9 100644 --- a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Inventory.php +++ b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Inventory.php @@ -6,45 +6,23 @@ * @license See LICENSE_DIVANTE.txt for license details. */ +declare(strict_types=1); + namespace Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product; +use Divante\VsbridgeIndexerCatalog\Model\Inventory\Fields as InventoryFields; use Magento\Framework\App\ResourceConnection; -use Magento\Store\Model\StoreManagerInterface; +use Magento\CatalogInventory\Api\StockConfigurationInterface; /** * Class Inventory */ class Inventory { - /** - * @var array + * @var StockConfigurationInterface */ - private $fields = [ - 'product_id', - 'item_id', - 'stock_id', - 'qty', - 'is_in_stock', - 'is_qty_decimal', - 'use_config_min_qty', - 'min_qty', - 'use_config_min_sale_qty', - 'min_sale_qty', - 'use_config_max_sale_qty', - 'max_sale_qty', - 'use_config_notify_stock_qty', - 'notify_stock_qty', - 'use_config_qty_increments', - 'backorders', - 'use_config_backorders', - 'qty_increments', - 'use_config_enable_qty_inc', - 'enable_qty_increments', - 'use_config_manage_stock', - 'manage_stock', - 'low_stock_date', - ]; + private $stockConfiguration; /** * @var ResourceConnection @@ -52,65 +30,57 @@ class Inventory private $resource; /** - * @var StoreManagerInterface + * @var InventoryFields */ - private $storeManager; + private $inventoryFields; /** * Inventory constructor. * - * @param StoreManagerInterface $storeManager + * @param StockConfigurationInterface $stockConfiguration + * @param InventoryFields $fields * @param ResourceConnection $resourceModel */ public function __construct( - StoreManagerInterface $storeManager, + StockConfigurationInterface $stockConfiguration, + InventoryFields $fields, ResourceConnection $resourceModel ) { + $this->inventoryFields = $fields; $this->resource = $resourceModel; - $this->storeManager = $storeManager; + $this->stockConfiguration = $stockConfiguration; } /** - * @param int $storeId * @param array $productIds * * @return array */ - public function loadInventoryData($storeId, array $productIds) + public function loadInventory(array $productIds): array { - return $this->getInventoryData($storeId, $productIds, $this->fields); + return $this->getInventoryData($productIds, $this->inventoryFields->getRequiredColumns()); } /** - * @param int $storeId * @param array $productIds * * @return array */ - public function loadChildrenData($storeId, array $productIds) + public function loadChildrenInventory(array $productIds): array { - $fields = [ - 'product_id', - 'is_in_stock', - 'min_qty', - 'notify_stock_qty', - 'use_config_notify_stock_qty', - 'qty', - ]; - - return $this->getInventoryData($storeId, $productIds, $fields); + return $this->getInventoryData($productIds, $this->inventoryFields->getChildRequiredColumns()); } /** - * @param int $storeId * @param array $productIds * @param array $fields * * @return array + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - private function getInventoryData($storeId, array $productIds, array $fields) + private function getInventoryData(array $productIds, array $fields): array { - $websiteId = $this->storeManager->getStore($storeId)->getWebsiteId(); + $websiteId = $this->getWebsiteId(); $connection = $this->resource->getConnection(); $select = $connection->select() @@ -136,4 +106,12 @@ private function getInventoryData($storeId, array $productIds, array $fields) return $connection->fetchAssoc($select); } + + /** + * @return int|null + */ + private function getWebsiteId() + { + return $this->stockConfiguration->getDefaultScopeId(); + } } diff --git a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Links.php b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Links.php index 5afaac87..8f6cfb8b 100644 --- a/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Links.php +++ b/src/module-vsbridge-indexer-catalog/Model/ResourceModel/Product/Links.php @@ -8,27 +8,15 @@ namespace Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product; -use Magento\Catalog\Model\Product\Link as ProductLink; +use Divante\VsbridgeIndexerCatalog\Model\Product\LinkTypeMapper; use Magento\Framework\App\ResourceConnection; use Magento\Framework\DB\Select; -use Magento\GroupedProduct\Model\ResourceModel\Product\Link as GroupedLink; /** * Class Links */ class Links { - /** - * Product link type mapping, used for references and validation - * - * @var array - */ - private $typeMap = [ - ProductLink::LINK_TYPE_RELATED => 'related', - ProductLink::LINK_TYPE_UPSELL => 'upsell', - ProductLink::LINK_TYPE_CROSSSELL => 'crosssell', - GroupedLink::LINK_TYPE_GROUPED => 'associated', - ]; /** * @var array @@ -45,6 +33,11 @@ class Links */ private $resource; + /** + * @var LinkTypeMapper + */ + private $linkTypeMapper; + /** * @var array */ @@ -53,10 +46,14 @@ class Links /** * Links constructor. * + * @param LinkTypeMapper $linkTypeMapper * @param ResourceConnection $resourceConnection */ - public function __construct(ResourceConnection $resourceConnection) - { + public function __construct( + LinkTypeMapper $linkTypeMapper, + ResourceConnection $resourceConnection + ) { + $this->linkTypeMapper = $linkTypeMapper; $this->resource = $resourceConnection; } @@ -93,15 +90,18 @@ public function getLinkedProduct(array $product) $linkProductList = []; foreach ($links[$productId] as $linkData) { - $typeId = $linkData['link_type_id']; - - $linkProductList[] = [ - 'sku' => $product['sku'], - 'link_type' => $this->getLinkType($typeId), - 'linked_product_sku' => $linkData['sku'], - 'linked_product_type' => $linkData['type_id'], - 'position' => (int)$linkData['position'], - ]; + $typeId = (int)$linkData['link_type_id']; + $linkType = $this->getLinkType($typeId); + + if ($linkType) { + $linkProductList[] = [ + 'sku' => $product['sku'], + 'link_type' => $linkType, + 'linked_product_sku' => $linkData['sku'], + 'linked_product_type' => $linkData['type_id'], + 'position' => (int)$linkData['position'], + ]; + } } return $linkProductList; @@ -117,11 +117,7 @@ public function getLinkedProduct(array $product) */ private function getLinkType($typeId) { - if (isset($this->typeMap[$typeId])) { - return $this->typeMap[$typeId]; - } - - return null; + return $this->linkTypeMapper->map($typeId); } /** @@ -185,6 +181,11 @@ private function joinPositionAttribute(Select $select) { $alias = 'link_position'; $attributePosition = $this->fetchPositionAttributeData(); + + if (empty($attributePosition)) { + return $select; + } + $table = $this->resource->getTableName($this->getAttributeTypeTable($attributePosition['type'])); $joinCondition = [ diff --git a/src/module-vsbridge-indexer-catalog/Model/ConfigSettings.php b/src/module-vsbridge-indexer-catalog/Model/Settings.php similarity index 86% rename from src/module-vsbridge-indexer-catalog/Model/ConfigSettings.php rename to src/module-vsbridge-indexer-catalog/Model/Settings.php index f9c52884..12d63e2b 100644 --- a/src/module-vsbridge-indexer-catalog/Model/ConfigSettings.php +++ b/src/module-vsbridge-indexer-catalog/Model/Settings.php @@ -8,16 +8,15 @@ namespace Divante\VsbridgeIndexerCatalog\Model; +use Divante\VsbridgeIndexerCatalog\Api\Data\CatalogConfigurationInterface; use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Config as ConfigResource; use Magento\Framework\App\Config\ScopeConfigInterface as ScopeConfigInterface; /** * Class ConfigSettings */ -class ConfigSettings +class Settings implements CatalogConfigurationInterface { - const CATALOG_SETTINGS_XML_PREFIX = 'vsbridge_indexer_settings/catalog_settings'; - /** * @var array */ @@ -53,7 +52,7 @@ public function __construct( } /** - * @return bool + * @inheritdoc */ public function useMagentoUrlKeys() { @@ -61,7 +60,15 @@ public function useMagentoUrlKeys() } /** - * @return bool + * @inheritdoc + */ + public function useUrlKeyToGenerateSlug() + { + return (bool) $this->getConfigParam('use_url_key_to_generate_slug'); + } + + /** + * @inheritdoc */ public function syncTierPrices() { @@ -69,9 +76,7 @@ public function syncTierPrices() } /** - * @param int $storeId - * - * @return array + * @inheritdoc */ public function getAllowedProductTypes($storeId) { @@ -97,7 +102,7 @@ private function getConfigParam(string $configField, $storeId = null) $key = $configField . (string) $storeId; if (!isset($this->settings[$key])) { - $path = self::CATALOG_SETTINGS_XML_PREFIX . '/' . $configField; + $path = CatalogConfigurationInterface::CATALOG_SETTINGS_XML_PREFIX . '/' . $configField; if ($storeId) { $configValue = $this->scopeConfig->getValue($path, 'stores', $storeId); @@ -111,9 +116,7 @@ private function getConfigParam(string $configField, $storeId = null) } /** - * - * @return array - * @throws \Exception + * @inheritdoc */ public function getAttributesUsedForSortBy() { @@ -128,9 +131,7 @@ public function getAttributesUsedForSortBy() } /** - * @param int $storeId - * - * @return string + * @inheritdoc */ public function getProductListDefaultSortBy($storeId) { diff --git a/src/module-vsbridge-indexer-catalog/Model/SlugGenerator.php b/src/module-vsbridge-indexer-catalog/Model/SlugGenerator.php index 9eaba5c4..839cceff 100644 --- a/src/module-vsbridge-indexer-catalog/Model/SlugGenerator.php +++ b/src/module-vsbridge-indexer-catalog/Model/SlugGenerator.php @@ -8,20 +8,19 @@ namespace Divante\VsbridgeIndexerCatalog\Model; +use Divante\VsbridgeIndexerCatalog\Api\SlugGeneratorInterface; + /** * Class SlugGenerator */ -class SlugGenerator +class SlugGenerator implements SlugGeneratorInterface { /** - * @param string $name - * @param int $id - * - * @return mixed|null|string|string[] + * @inheritdoc */ - public function generate($name, $id) + public function generate($text, $id) { - $text = $name . '-' . $id; + $text = $text . '-' . $id; return $this->slugify($text); } diff --git a/src/module-vsbridge-indexer-catalog/Model/TierPriceProcessor.php b/src/module-vsbridge-indexer-catalog/Model/TierPriceProcessor.php index c06c440a..5e2bab75 100644 --- a/src/module-vsbridge-indexer-catalog/Model/TierPriceProcessor.php +++ b/src/module-vsbridge-indexer-catalog/Model/TierPriceProcessor.php @@ -10,9 +10,9 @@ use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\TierPrices as TierPricesResource; use Divante\VsbridgeIndexerCatalog\Model\ResourceModel\Product\AttributeDataProvider; -use Magento\Catalog\Model\Product\Visibility; use Magento\Customer\Model\Group; use Magento\Store\Model\StoreManagerInterface; +use Divante\VsbridgeIndexerCatalog\Api\Data\CatalogConfigurationInterface; /** * Class TierPriceProcessor @@ -40,21 +40,21 @@ class TierPriceProcessor private $productMetaData; /** - * @var ConfigSettings + * @var CatalogConfigurationInterface */ private $configSettings; /** * PriceData constructor. * - * @param ConfigSettings $configSettings + * @param CatalogConfigurationInterface $configSettings * @param TierPricesResource $tierPricesResource * @param StoreManagerInterface $storeManager * @param ProductMetaData $productMetaData * @param AttributeDataProvider $config */ public function __construct( - ConfigSettings $configSettings, + CatalogConfigurationInterface $configSettings, TierPricesResource $tierPricesResource, StoreManagerInterface $storeManager, ProductMetaData $productMetaData, diff --git a/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Attribute/Save/UpdateAttributeData.php b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Attribute/Save/UpdateAttributeDataPlugin.php similarity index 94% rename from src/module-vsbridge-indexer-catalog/Plugin/Indexer/Attribute/Save/UpdateAttributeData.php rename to src/module-vsbridge-indexer-catalog/Plugin/Indexer/Attribute/Save/UpdateAttributeDataPlugin.php index 3abce733..3f0f7237 100644 --- a/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Attribute/Save/UpdateAttributeData.php +++ b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Attribute/Save/UpdateAttributeDataPlugin.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. @@ -13,9 +13,9 @@ use Magento\Catalog\Model\ResourceModel\Eav\Attribute; /** - * Class UpdateAttributeData + * Class UpdateAttributeDataPlugin */ -class UpdateAttributeData +class UpdateAttributeDataPlugin { /** * @var AttributeProcessor diff --git a/src/module-vsbridge-indexer-catalog/Plugin/Indexer/CatalogInventory/ProductsForReindex.php b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/CatalogInventory/ProductsForReindex.php new file mode 100644 index 00000000..4f521ac5 --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/CatalogInventory/ProductsForReindex.php @@ -0,0 +1,45 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Plugin\Indexer\CatalogInventory; + +/** + * Class ProductsForReindex + */ +class ProductsForReindex +{ + /** + * @var array + */ + private $productsForReindex = []; + + /** + * @param array $items + * @return void + */ + public function setProducts(array $items) + { + $this->productsForReindex = $items; + } + + /** + * @return array + */ + public function getProducts() + { + return $this->productsForReindex; + } + + /** + * @return void + */ + public function clear() + { + $this->productsForReindex = []; + } +} diff --git a/src/module-vsbridge-indexer-catalog/Plugin/Indexer/CatalogInventory/QtyCorrectPlugin.php b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/CatalogInventory/QtyCorrectPlugin.php new file mode 100644 index 00000000..6fc838f4 --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/CatalogInventory/QtyCorrectPlugin.php @@ -0,0 +1,58 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Plugin\Indexer\CatalogInventory; + +use Magento\CatalogInventory\Model\ResourceModel\QtyCounterInterface; + +/** + * Class QtyCorrectPlugin + */ +class QtyCorrectPlugin +{ + /** + * @var ProductsForReindex + */ + private $productsForReindex; + + /** + * ProcessStockChangedPlugin constructor. + * + * @param ProductsForReindex $itemsForReindex + */ + public function __construct( + ProductsForReindex $itemsForReindex + ) { + $this->productsForReindex = $itemsForReindex; + } + + /** + * @param QtyCounterInterface $subject + * @param callable $proceed + * @param array $items + * @param int $websiteId + * @param string $operator + * + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function aroundCorrectItemsQty( + QtyCounterInterface $subject, + callable $proceed, + array $items, + $websiteId, + $operator + ) { + if (!empty($items)) { + $productIds = array_keys($items); + $this->productsForReindex->setProducts($productIds); + } + + $proceed($items, $websiteId, $operator); + } +} diff --git a/src/module-vsbridge-indexer-catalog/Plugin/Indexer/CatalogInventory/ReindexQuoteInventoryObserverPlugin.php b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/CatalogInventory/ReindexQuoteInventoryObserverPlugin.php new file mode 100644 index 00000000..4e82b1b2 --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/CatalogInventory/ReindexQuoteInventoryObserverPlugin.php @@ -0,0 +1,58 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Plugin\Indexer\CatalogInventory; + +use Divante\VsbridgeIndexerCatalog\Model\Indexer\ProductProcessor; +use Magento\CatalogInventory\Observer\ReindexQuoteInventoryObserver; + +/** + * Class ReindexQuoteInventoryObserverPlugin + */ +class ReindexQuoteInventoryObserverPlugin +{ + /** + * @var ProductsForReindex + */ + private $productsForReindex; + + /** + * @var ProductProcessor + */ + private $productProcessor; + + /** + * ProcessStockChangedPlugin constructor. + * + * @param ProductsForReindex $itemsForReindex + * @param ProductProcessor $processor + */ + public function __construct( + ProductsForReindex $itemsForReindex, + ProductProcessor $processor + ) { + $this->productsForReindex = $itemsForReindex; + $this->productProcessor = $processor; + } + + /** + * @param ReindexQuoteInventoryObserver $subject + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * + * @return void + */ + public function afterExecute(ReindexQuoteInventoryObserver $subject) + { + $products = $this->productsForReindex->getProducts(); + + if (!empty($products)) { + $this->productProcessor->reindexList($products); + $this->productsForReindex->clear(); + } + } +} diff --git a/src/module-vsbridge-indexer-catalog/Plugin/Indexer/CatalogInventory/RevertQuoteInventoryObserverPlugin.php b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/CatalogInventory/RevertQuoteInventoryObserverPlugin.php new file mode 100644 index 00000000..de679c79 --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/CatalogInventory/RevertQuoteInventoryObserverPlugin.php @@ -0,0 +1,58 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Plugin\Indexer\CatalogInventory; + +use Divante\VsbridgeIndexerCatalog\Model\Indexer\ProductProcessor; +use Magento\CatalogInventory\Observer\RevertQuoteInventoryObserver; + +/** + * Class RevertQuoteInventoryObserverPlugin + */ +class RevertQuoteInventoryObserverPlugin +{ + /** + * @var ProductsForReindex + */ + private $productsForReindex; + + /** + * @var ProductProcessor + */ + private $productProcessor; + + /** + * ProcessStockChangedPlugin constructor. + * + * @param ProductsForReindex $itemsForReindex + * @param ProductProcessor $processor + */ + public function __construct( + ProductsForReindex $itemsForReindex, + ProductProcessor $processor + ) { + $this->productsForReindex = $itemsForReindex; + $this->productProcessor = $processor; + } + + /** + * @param RevertQuoteInventoryObserver $subject + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * + * @return void + */ + public function afterExecute(RevertQuoteInventoryObserver $subject) + { + $products = $this->productsForReindex->getProducts(); + + if (!empty($products)) { + $this->productProcessor->reindexList($products); + $this->productsForReindex->clear(); + } + } +} diff --git a/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Category/Save/UpdateCategoryData.php b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Category/Save/UpdateCategoryDataPlugin.php similarity index 89% rename from src/module-vsbridge-indexer-catalog/Plugin/Indexer/Category/Save/UpdateCategoryData.php rename to src/module-vsbridge-indexer-catalog/Plugin/Indexer/Category/Save/UpdateCategoryDataPlugin.php index 3fc5652a..6493ac48 100644 --- a/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Category/Save/UpdateCategoryData.php +++ b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Category/Save/UpdateCategoryDataPlugin.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. @@ -12,9 +12,9 @@ use Magento\Catalog\Model\Category; /** - * Class UpdateCategoryData + * Class UpdateCategoryDataPlugin */ -class UpdateCategoryData +class UpdateCategoryDataPlugin { /** * @var CategoryProcessor diff --git a/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Category/Save/UpdateProductPlugin.php b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Category/Save/UpdateProductPlugin.php new file mode 100644 index 00000000..b11a579b --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Category/Save/UpdateProductPlugin.php @@ -0,0 +1,50 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCatalog\Plugin\Indexer\Category\Save; + +use Divante\VsbridgeIndexerCatalog\Model\Indexer\ProductCategoryProcessor; + +/** + * Class UpdateProductPlugin + */ +class UpdateProductPlugin +{ + /** + * @var ProductCategoryProcessor + */ + private $productCategoryProcessor; + + /** + * UpdateProduct constructor. + * + * @param ProductCategoryProcessor $processor + */ + public function __construct(ProductCategoryProcessor $processor) + { + $this->productCategoryProcessor = $processor; + } + + /** + * Update product category data in ES after changing category products + * + * @param \Magento\Catalog\Model\Category $category + * @return \Magento\Catalog\Model\Category + */ + public function afterSave(\Magento\Catalog\Model\Category $category) + { + $isChangedProductList = $category->getData('is_changed_product_list'); + + if ($isChangedProductList && !$this->productCategoryProcessor->isIndexerScheduled()) { + $affectedProductIds = $category->getAffectedProductIds(); + $this->productCategoryProcessor->reindexList($affectedProductIds); + } + + return $category; + } +} diff --git a/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Product/Save/UpdateProductData.php b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Product/Save/UpdateProductDataPlugin.php similarity index 89% rename from src/module-vsbridge-indexer-catalog/Plugin/Indexer/Product/Save/UpdateProductData.php rename to src/module-vsbridge-indexer-catalog/Plugin/Indexer/Product/Save/UpdateProductDataPlugin.php index 0dab83d9..b9a409fe 100644 --- a/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Product/Save/UpdateProductData.php +++ b/src/module-vsbridge-indexer-catalog/Plugin/Indexer/Product/Save/UpdateProductDataPlugin.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. @@ -12,9 +12,9 @@ use Magento\Catalog\Model\Product; /** - * Class UpdateProductData + * Class UpdateProductDataPlugin */ -class UpdateProductData +class UpdateProductDataPlugin { /** * @var ProductProcessor diff --git a/src/module-vsbridge-indexer-catalog/etc/adminhtml/system.xml b/src/module-vsbridge-indexer-catalog/etc/adminhtml/system.xml index fd7c07ec..9338d465 100644 --- a/src/module-vsbridge-indexer-catalog/etc/adminhtml/system.xml +++ b/src/module-vsbridge-indexer-catalog/etc/adminhtml/system.xml @@ -12,10 +12,23 @@
+ + 1 + - - Set base on your setting products.useShortCatalogUrls from vsf. If you change this setting on vsf you have to reindex all product and categories for vsf. + + Use Magento Url Key attribute for url_key and slug field (for products and categories). Url Keys have to be unique. + IF you select "Yes" slug will be generated base on Magento url_key attribute. + + Magento\Config\Model\Config\Source\Yesno + + + + By default slug (and url_key) is generated base on product/category NAME and ID. Magento\Config\Model\Config\Source\Yesno + + 0 + @@ -25,7 +38,7 @@ Divante\VsbridgeIndexerCatalog\Model\Config\Source\ProductType - + Magento\Config\Model\Config\Source\Yesno diff --git a/src/module-vsbridge-indexer-catalog/etc/config.xml b/src/module-vsbridge-indexer-catalog/etc/config.xml new file mode 100644 index 00000000..e9d196e4 --- /dev/null +++ b/src/module-vsbridge-indexer-catalog/etc/config.xml @@ -0,0 +1,20 @@ + + + + + + + 0 + 0 + 0 + + + + diff --git a/src/module-vsbridge-indexer-catalog/etc/di.xml b/src/module-vsbridge-indexer-catalog/etc/di.xml index b6bd26d7..8733bebf 100644 --- a/src/module-vsbridge-indexer-catalog/etc/di.xml +++ b/src/module-vsbridge-indexer-catalog/etc/di.xml @@ -1,5 +1,19 @@ + + + + + + + + + + Divante\VsbridgeIndexerCatalog\Model\LoadChildrenInventory + + + @@ -7,6 +21,13 @@ product + + + vue_storefront_catalog + product + + @@ -27,6 +48,11 @@ Divante\VsbridgeIndexerCatalog\Indexer\ProductIndexerHandlerVirtual + + + Divante\VsbridgeIndexerCatalog\Indexer\ProductCategoryHandlerVirtual + + Divante\VsbridgeIndexerCatalog\Indexer\CategoryIndexerHandlerVirtual @@ -76,14 +102,26 @@ - + - + + - + + + + + + + + + + + + diff --git a/src/module-vsbridge-indexer-catalog/etc/indexer.xml b/src/module-vsbridge-indexer-catalog/etc/indexer.xml index d1322e28..65de0869 100644 --- a/src/module-vsbridge-indexer-catalog/etc/indexer.xml +++ b/src/module-vsbridge-indexer-catalog/etc/indexer.xml @@ -4,10 +4,6 @@ class="Divante\VsbridgeIndexerCatalog\Model\Indexer\Product"> Vsbridge Product Indexer Update Products data in Elastic - - - - @@ -19,4 +15,9 @@ Vsbridge Attributes Indexer Update Product Attributes Meta Data in Elastic + + Vsbridge Product Category Indexer + Partial Product update - update category, category_ids fields in ES + diff --git a/src/module-vsbridge-indexer-catalog/etc/mview.xml b/src/module-vsbridge-indexer-catalog/etc/mview.xml index 291cab2b..c71425ed 100644 --- a/src/module-vsbridge-indexer-catalog/etc/mview.xml +++ b/src/module-vsbridge-indexer-catalog/etc/mview.xml @@ -11,6 +11,14 @@
+
+
+ + + + + +
diff --git a/src/module-vsbridge-indexer-cms/Api/ContentProcessorInterface.php b/src/module-vsbridge-indexer-cms/Api/ContentProcessorInterface.php new file mode 100644 index 00000000..b7586c7b --- /dev/null +++ b/src/module-vsbridge-indexer-cms/Api/ContentProcessorInterface.php @@ -0,0 +1,26 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCms\Api; + +use Magento\Framework\Filter\Template as TemplateFilter; + +/** + * Interface ContentProcessorInterface + */ +interface ContentProcessorInterface +{ + /** + * @param TemplateFilter $templateFilter + * @param string $content + * + * @return string|array + * @throws \Exception + */ + public function parse(TemplateFilter $templateFilter, string $content); +} diff --git a/src/module-vsbridge-indexer-cms/Index/Mapping/CmsBlock.php b/src/module-vsbridge-indexer-cms/Index/Mapping/CmsBlock.php index 87b3241f..cd816e68 100644 --- a/src/module-vsbridge-indexer-cms/Index/Mapping/CmsBlock.php +++ b/src/module-vsbridge-indexer-cms/Index/Mapping/CmsBlock.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. @@ -23,11 +23,6 @@ class CmsBlock implements MappingInterface */ private $eventManager; - /** - * @var string - */ - private $type; - /** * CmsBlock constructor. * @@ -38,22 +33,6 @@ public function __construct(EventManager $eventManager) $this->eventManager = $eventManager; } - /** - * @inheritdoc - */ - public function setType(string $type) - { - $this->type = $type; - } - - /** - * @return string - */ - public function getType() - { - return $this->type; - } - /** * @inheritdoc */ diff --git a/src/module-vsbridge-indexer-cms/Index/Mapping/CmsPage.php b/src/module-vsbridge-indexer-cms/Index/Mapping/CmsPage.php index 209b5913..7efebec7 100644 --- a/src/module-vsbridge-indexer-cms/Index/Mapping/CmsPage.php +++ b/src/module-vsbridge-indexer-cms/Index/Mapping/CmsPage.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. @@ -23,11 +23,6 @@ class CmsPage implements MappingInterface */ private $eventManager; - /** - * @var string - */ - private $type; - /** * @var array */ @@ -52,22 +47,6 @@ public function __construct(EventManager $eventManager) $this->eventManager = $eventManager; } - /** - * @inheritdoc - */ - public function setType(string $type) - { - $this->type = $type; - } - - /** - * @inheritdoc - */ - public function getType() - { - return $this->type; - } - /** * @inheritdoc */ diff --git a/src/module-vsbridge-indexer-cms/Model/ContentProcessor.php b/src/module-vsbridge-indexer-cms/Model/ContentProcessor.php new file mode 100644 index 00000000..93f4a88e --- /dev/null +++ b/src/module-vsbridge-indexer-cms/Model/ContentProcessor.php @@ -0,0 +1,27 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCms\Model; + +use Divante\VsbridgeIndexerCms\Api\ContentProcessorInterface; +use Magento\Framework\Filter\Template as TemplateFilter; + +/** + * Class ContentProcessor + */ +class ContentProcessor implements ContentProcessorInterface +{ + + /** + * @inheritdoc + */ + public function parse(TemplateFilter $templateFilter, string $content) + { + return $templateFilter->filter($content); + } +} diff --git a/src/module-vsbridge-indexer-cms/Model/Indexer/Action/CmsBlock.php b/src/module-vsbridge-indexer-cms/Model/Indexer/Action/CmsBlock.php index ad293fab..5a76672a 100644 --- a/src/module-vsbridge-indexer-cms/Model/Indexer/Action/CmsBlock.php +++ b/src/module-vsbridge-indexer-cms/Model/Indexer/Action/CmsBlock.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. @@ -8,7 +8,9 @@ namespace Divante\VsbridgeIndexerCms\Model\Indexer\Action; +use Divante\VsbridgeIndexerCms\Api\ContentProcessorInterface; use Divante\VsbridgeIndexerCms\Model\ResourceModel\CmsBlock as CmsBlockResource; + use Magento\Cms\Model\Template\FilterProvider; use Magento\Framework\App\Area; use Magento\Framework\App\AreaList; @@ -33,21 +35,29 @@ class CmsBlock */ private $areaList; + /** + * @var ContentProcessorInterface + */ + private $contentProcessor; + /** * CmsBlock constructor. * * @param AreaList $areaList + * @param ContentProcessorInterface $contentProcessor * @param CmsBlockResource $cmsBlockResource * @param FilterProvider $filterProvider */ public function __construct( AreaList $areaList, + ContentProcessorInterface $contentProcessor, CmsBlockResource $cmsBlockResource, FilterProvider $filterProvider ) { $this->areaList = $areaList; $this->filterProvider = $filterProvider; $this->resourceModel = $cmsBlockResource; + $this->contentProcessor = $contentProcessor; } /** @@ -59,7 +69,7 @@ public function __construct( public function rebuild($storeId = 1, array $blockIds = []) { $this->areaList->getArea(Area::AREA_FRONTEND)->load(Area::PART_DESIGN); - + $templateFilter = $this->filterProvider->getBlockFilter()->setStoreId($storeId); $lastBlockId = 0; do { @@ -68,9 +78,7 @@ public function rebuild($storeId = 1, array $blockIds = []) foreach ($cmsBlocks as $blockData) { $lastBlockId = $blockData['block_id']; $blockData['id'] = $blockData['block_id']; - $blockData['content'] = - $this->filterProvider->getBlockFilter()->setStoreId($storeId)->filter($blockData['content']); - + $blockData['content'] = $this->contentProcessor->parse($templateFilter, $blockData['content']); $blockData['active'] = (bool)$blockData['is_active']; unset($blockData['creation_time'], $blockData['update_time'], $blockData['block_id']); diff --git a/src/module-vsbridge-indexer-cms/Model/Indexer/Action/CmsPage.php b/src/module-vsbridge-indexer-cms/Model/Indexer/Action/CmsPage.php index 9ae6bf42..60c229fb 100644 --- a/src/module-vsbridge-indexer-cms/Model/Indexer/Action/CmsPage.php +++ b/src/module-vsbridge-indexer-cms/Model/Indexer/Action/CmsPage.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. @@ -8,7 +8,9 @@ namespace Divante\VsbridgeIndexerCms\Model\Indexer\Action; +use Divante\VsbridgeIndexerCms\Api\ContentProcessorInterface; use Divante\VsbridgeIndexerCms\Model\ResourceModel\CmsPage as CmsPageResource; + use Magento\Cms\Model\Template\FilterProvider; use Magento\Framework\App\Area; use Magento\Framework\App\AreaList; @@ -28,21 +30,34 @@ class CmsPage */ private $filterProvider; + /** + * @var AreaList + */ + private $areaList; + + /** + * @var ContentProcessorInterface + */ + private $contentProcessor; + /** * CmsBlock constructor. * * @param AreaList $areaList + * @param ContentProcessorInterface $contentProcessor * @param CmsPageResource $cmsBlockResource * @param FilterProvider $filterProvider */ public function __construct( AreaList $areaList, + ContentProcessorInterface $contentProcessor, CmsPageResource $cmsBlockResource, FilterProvider $filterProvider ) { $this->areaList = $areaList; - $this->filterProvider = $filterProvider; $this->resourceModel = $cmsBlockResource; + $this->filterProvider = $filterProvider; + $this->contentProcessor = $contentProcessor; } /** @@ -54,6 +69,7 @@ public function __construct( public function rebuild($storeId = 1, array $pageIds = []) { $this->areaList->getArea(Area::AREA_FRONTEND)->load(Area::PART_DESIGN); + $templateFilter = $this->filterProvider->getPageFilter()->setStoreId($storeId); $lastPageId = 0; do { @@ -62,8 +78,7 @@ public function rebuild($storeId = 1, array $pageIds = []) foreach ($cmsPages as $pageData) { $lastPageId = $pageData['page_id']; $pageData['id'] = $pageData['page_id']; - $pageData['content'] = - $this->filterProvider->getBlockFilter()->setStoreId($storeId)->filter($pageData['content']); + $pageData['content'] = $this->contentProcessor->parse($templateFilter, $pageData['content']); $pageData['active'] = (bool)$pageData['is_active']; unset($pageData['creation_time'], $pageData['update_time'], $pageData['page_id']); diff --git a/src/module-vsbridge-indexer-cms/Model/Indexer/CmsBlock.php b/src/module-vsbridge-indexer-cms/Model/Indexer/CmsBlock.php index 4f5b5571..08790d7c 100644 --- a/src/module-vsbridge-indexer-cms/Model/Indexer/CmsBlock.php +++ b/src/module-vsbridge-indexer-cms/Model/Indexer/CmsBlock.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-cms/Model/Indexer/CmsPage.php b/src/module-vsbridge-indexer-cms/Model/Indexer/CmsPage.php index f82d2883..dd31f631 100644 --- a/src/module-vsbridge-indexer-cms/Model/Indexer/CmsPage.php +++ b/src/module-vsbridge-indexer-cms/Model/Indexer/CmsPage.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-cms/Model/ResourceModel/CmsBlock.php b/src/module-vsbridge-indexer-cms/Model/ResourceModel/CmsBlock.php index 4eb54c16..592e1521 100644 --- a/src/module-vsbridge-indexer-cms/Model/ResourceModel/CmsBlock.php +++ b/src/module-vsbridge-indexer-cms/Model/ResourceModel/CmsBlock.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-cms/Model/ResourceModel/CmsPage.php b/src/module-vsbridge-indexer-cms/Model/ResourceModel/CmsPage.php index fc523a76..c7ba9026 100644 --- a/src/module-vsbridge-indexer-cms/Model/ResourceModel/CmsPage.php +++ b/src/module-vsbridge-indexer-cms/Model/ResourceModel/CmsPage.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-cms/Plugin/Indexer/Block/Save/UpdateCmsBlock.php b/src/module-vsbridge-indexer-cms/Plugin/Indexer/Block/Save/UpdateCmsBlock.php index 65ed9273..490f15d2 100644 --- a/src/module-vsbridge-indexer-cms/Plugin/Indexer/Block/Save/UpdateCmsBlock.php +++ b/src/module-vsbridge-indexer-cms/Plugin/Indexer/Block/Save/UpdateCmsBlock.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-cms/Plugin/Indexer/Page/Save/UpdateCmsPage.php b/src/module-vsbridge-indexer-cms/Plugin/Indexer/Page/Save/UpdateCmsPage.php index 8057a585..b085a30e 100644 --- a/src/module-vsbridge-indexer-cms/Plugin/Indexer/Page/Save/UpdateCmsPage.php +++ b/src/module-vsbridge-indexer-cms/Plugin/Indexer/Page/Save/UpdateCmsPage.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-cms/etc/di.xml b/src/module-vsbridge-indexer-cms/etc/di.xml index 0798815e..37d9ccf5 100644 --- a/src/module-vsbridge-indexer-cms/etc/di.xml +++ b/src/module-vsbridge-indexer-cms/etc/di.xml @@ -1,5 +1,6 @@ + diff --git a/src/module-vsbridge-indexer-core/Api/Client/ClientInterface.php b/src/module-vsbridge-indexer-core/Api/Client/ClientInterface.php index ee43a25d..ea44d531 100644 --- a/src/module-vsbridge-indexer-core/Api/Client/ClientInterface.php +++ b/src/module-vsbridge-indexer-core/Api/Client/ClientInterface.php @@ -8,6 +8,8 @@ namespace Divante\VsbridgeIndexerCore\Api\Client; +use Divante\VsbridgeIndexerCore\Exception\ConnectionDisabledException; + /** * Interface ClientInterface */ @@ -17,14 +19,16 @@ interface ClientInterface * @param array $bulkParams * * @return array + * @throws ConnectionDisabledException */ public function bulk(array $bulkParams); /** * @param string $indexName - * @param array $indexSettings + * @param array $indexSettings * * @return void + * @throws ConnectionDisabledException */ public function createIndex($indexName, array $indexSettings); @@ -32,6 +36,7 @@ public function createIndex($indexName, array $indexSettings); * @param string $indexName * * @return void + * @throws ConnectionDisabledException */ public function refreshIndex($indexName); @@ -39,6 +44,7 @@ public function refreshIndex($indexName); * @param string $indexName * * @return bool + * @throws ConnectionDisabledException */ public function indexExists($indexName); @@ -46,18 +52,23 @@ public function indexExists($indexName); * @param string $indexName * * @return array + * @throws ConnectionDisabledException */ public function deleteIndex($indexName); /** * @param string $indexName * @param string $type - * @param array $mapping + * @param array $mapping + * + * @throws ConnectionDisabledException */ public function putMapping($indexName, $type, array $mapping); /** * @param array $params + * + * @throws ConnectionDisabledException */ public function deleteByQuery(array $params); } diff --git a/src/module-vsbridge-indexer-core/Api/Mapping/FieldInterface.php b/src/module-vsbridge-indexer-core/Api/Mapping/FieldInterface.php index 5ce3904a..09de1295 100644 --- a/src/module-vsbridge-indexer-core/Api/Mapping/FieldInterface.php +++ b/src/module-vsbridge-indexer-core/Api/Mapping/FieldInterface.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-core/Api/MappingInterface.php b/src/module-vsbridge-indexer-core/Api/MappingInterface.php index 5a06ecb1..93577b20 100644 --- a/src/module-vsbridge-indexer-core/Api/MappingInterface.php +++ b/src/module-vsbridge-indexer-core/Api/MappingInterface.php @@ -13,18 +13,6 @@ */ interface MappingInterface { - /** - * @param string $type - * - * @return $this - */ - public function setType(string $type); - - /** - * @return string - */ - public function getType(); - /** * @return array */ diff --git a/src/module-vsbridge-indexer-core/Cache/Logger/Handler/Info.php b/src/module-vsbridge-indexer-core/Cache/Logger/Handler/Info.php index 0465ff23..b4546e05 100644 --- a/src/module-vsbridge-indexer-core/Cache/Logger/Handler/Info.php +++ b/src/module-vsbridge-indexer-core/Cache/Logger/Handler/Info.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-core/Config/GeneralSettings.php b/src/module-vsbridge-indexer-core/Config/GeneralSettings.php index 4e8585c5..125c20b6 100644 --- a/src/module-vsbridge-indexer-core/Config/GeneralSettings.php +++ b/src/module-vsbridge-indexer-core/Config/GeneralSettings.php @@ -64,6 +64,16 @@ public function getStoresToIndex() return $stores; } + /** + * Check if ES indexing enabled + * + * @return bool + */ + public function isEnabled() + { + return (bool)$this->getConfigParam('enable'); + } + /** * @param string $configField * diff --git a/src/module-vsbridge-indexer-core/Config/IndicesSettings.php b/src/module-vsbridge-indexer-core/Config/IndicesSettings.php index 768256d7..4691fc8f 100644 --- a/src/module-vsbridge-indexer-core/Config/IndicesSettings.php +++ b/src/module-vsbridge-indexer-core/Config/IndicesSettings.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. @@ -40,6 +40,14 @@ public function getIndexNamePrefix() return (string) $this->getConfigParam('index_name'); } + /** + * @return string + */ + public function getIndexIdentifier() + { + return (string) $this->getConfigParam('index_identifier'); + } + /** * @return int */ diff --git a/src/module-vsbridge-indexer-core/Console/Command/RebuildEsIndexCommand.php b/src/module-vsbridge-indexer-core/Console/Command/RebuildEsIndexCommand.php new file mode 100644 index 00000000..02a3c17f --- /dev/null +++ b/src/module-vsbridge-indexer-core/Console/Command/RebuildEsIndexCommand.php @@ -0,0 +1,177 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCore\Console\Command; + +use Magento\Backend\App\Area\FrontNameResolver; +use Symfony\Component\Console\Command\Command; +use Magento\Framework\Indexer\IndexerInterface; +use Divante\VsbridgeIndexerCatalog\Model\Indexer\ProductCategoryProcessor; +use Magento\Framework\Console\Cli; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Magento\Framework\Exception\LocalizedException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Divante\VsbridgeIndexerCore\Index\IndexOperations; +use Magento\Store\Model\StoreManagerInterface; +use Magento\Framework\Indexer\IndexerRegistry; + +/** + * Class IndexerReindexCommand + */ +class RebuildEsIndexCommand extends Command +{ + const INPUT_STORE = 'store'; + const INPUT_DELETE_INDEX = 'delete-index'; + + const INDEX_IDENTIFIER = 'vue_storefront_catalog'; + + /** + * @var \Magento\Indexer\Model\Indexer\CollectionFactory + */ + private $collectionFactory; + + /** + * @var IndexOperations + */ + private $indexOperations; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var IndexerRegistry + */ + private $indexerRegistry; + + /** + * @var \Magento\Framework\App\State + */ + private $state; + + /** + * Constructor + * + * @param \Magento\Indexer\Model\Indexer\CollectionFactory|null $collectionFactory + */ + public function __construct( + IndexerRegistry $indexerRegistry, + IndexOperations\Proxy $indexOperations, + StoreManagerInterface\Proxy $storeManager, + \Magento\Framework\App\State\Proxy $state, + \Magento\Indexer\Model\Indexer\CollectionFactory\Proxy $collectionFactory + ) { + $this->indexerRegistry = $indexerRegistry; + $this->collectionFactory = $collectionFactory; + $this->indexOperations = $indexOperations; + $this->storeManager = $storeManager; + $this->state = $state; + parent::__construct(); + } + + /** + * @inheritdoc + */ + protected function configure() + { + $this->setName('vsbridge:reindex') + ->setDescription('Rebuild indexer in ES.'); + + $this->addOption( + self::INPUT_STORE, + null, + InputOption::VALUE_REQUIRED, + 'Store ID' + ); + + + $this->addOption( + self::INPUT_DELETE_INDEX, + null, + InputOption::VALUE_NONE, + 'Delete previous index and create new one (with new mapping)' + ); + + parent::configure(); + } + + /** + * @inheritdoc + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $storeId = (int)$input->getOption(self::INPUT_STORE); + + if ($storeId) { + $this->setAreaCode(); + $store = $this->storeManager->getStore($storeId); + $deleteIndex = $input->getOption(self::INPUT_DELETE_INDEX); + + if ($deleteIndex) { + $this->indexOperations->deleteIndex(self::INDEX_IDENTIFIER, $store); + $this->indexOperations->createIndex(self::INDEX_IDENTIFIER, $store); + } + + $returnValue = Cli::RETURN_FAILURE; + + foreach ($this->getIndexers($input) as $indexer) { + try { + $startTime = microtime(true); + + $indexer->reindexAll(); + + $resultTime = microtime(true) - $startTime; + $output->writeln( + $indexer->getTitle() . ' index has been rebuilt successfully in ' . gmdate('H:i:s', $resultTime) + ); + $returnValue = Cli::RETURN_SUCCESS; + } catch (LocalizedException $e) { + $output->writeln($e->getMessage()); + } catch (\Exception $e) { + $output->writeln($indexer->getTitle() . ' indexer process unknown error:'); + $output->writeln($e->getMessage()); + } + } + + return $returnValue; + } + } + + /** + * @return void + */ + private function setAreaCode() + { + try { + $this->state->setAreaCode(FrontNameResolver::AREA_CODE); + } catch (\Exception $e) { + } + } + + /** + * @return IndexerInterface[] + */ + protected function getIndexers(InputInterface $input) + { + /** @var IndexerInterface[] */ + $indexers = $this->collectionFactory->create()->getItems(); + unset($indexers[ProductCategoryProcessor::INDEXER_ID]); + $vsbridgeIndexers = []; + + foreach ($indexers as $indexer) { + if (substr($indexer->getId(), 0, 9) === 'vsbridge_') { + $vsbridgeIndexers[] = $indexer; + } + } + + return $vsbridgeIndexers; + } +} diff --git a/src/module-vsbridge-indexer-core/Elasticsearch/Client.php b/src/module-vsbridge-indexer-core/Elasticsearch/Client.php index 6d3f81cd..2af1a9a8 100644 --- a/src/module-vsbridge-indexer-core/Elasticsearch/Client.php +++ b/src/module-vsbridge-indexer-core/Elasticsearch/Client.php @@ -11,27 +11,49 @@ use Divante\VsbridgeIndexerCore\Api\Client\BuilderInterface as ClientBuilder; use Divante\VsbridgeIndexerCore\Api\Client\ConfigurationInterface as ClientConfiguration; use Divante\VsbridgeIndexerCore\Api\Client\ClientInterface; +use Divante\VsbridgeIndexerCore\Config\GeneralSettings; +use Divante\VsbridgeIndexerCore\Exception\ConnectionDisabledException; /** * Class Client */ class Client implements ClientInterface { + /** + * @var \Divante\VsbridgeIndexerCore\Config\GeneralSettings + */ + protected $config; /** * @var \Elasticsearch\Client */ - private $esClient = null; + private $client; + + /** + * @var ClientBuilder + */ + private $clientBuilder; + + /** + * @var ClientConfiguration + */ + private $clientConfiguration; /** * Client constructor. * - * @param ClientBuilder $clientBuilder + * @param ClientBuilder $clientBuilder * @param ClientConfiguration $clientConfiguration + * @param GeneralSettings $config */ - public function __construct(ClientBuilder $clientBuilder, ClientConfiguration $clientConfiguration) - { - $this->esClient = $clientBuilder->build($clientConfiguration->getOptions()); + public function __construct( + ClientBuilder $clientBuilder, + ClientConfiguration $clientConfiguration, + GeneralSettings $config + ) { + $this->clientBuilder = $clientBuilder; + $this->clientConfiguration = $clientConfiguration; + $this->config = $config; } /** @@ -39,7 +61,7 @@ public function __construct(ClientBuilder $clientBuilder, ClientConfiguration $c */ public function bulk(array $bulkParams) { - return $this->esClient->bulk($bulkParams); + return $this->getClient()->bulk($bulkParams); } /** @@ -47,10 +69,10 @@ public function bulk(array $bulkParams) */ public function createIndex($indexName, array $indexSettings) { - $this->esClient->indices()->create( + $this->getClient()->indices()->create( [ 'index' => $indexName, - 'body' => $indexSettings, + 'body' => $indexSettings, ] ); } @@ -60,7 +82,7 @@ public function createIndex($indexName, array $indexSettings) */ public function refreshIndex($indexName) { - $this->esClient->indices()->refresh(['index' => $indexName]); + $this->getClient()->indices()->refresh(['index' => $indexName]); } /** @@ -68,7 +90,7 @@ public function refreshIndex($indexName) */ public function indexExists($indexName) { - return $this->esClient->indices()->exists(['index' => $indexName]); + return $this->getClient()->indices()->exists(['index' => $indexName]); } /** @@ -76,7 +98,7 @@ public function indexExists($indexName) */ public function deleteIndex($indexName) { - return $this->esClient->indices()->delete(['index' => $indexName]); + return $this->getClient()->indices()->delete(['index' => $indexName]); } /** @@ -84,11 +106,11 @@ public function deleteIndex($indexName) */ public function putMapping($indexName, $type, array $mapping) { - $this->esClient->indices()->putMapping( + $this->getClient()->indices()->putMapping( [ 'index' => $indexName, - 'type' => $type, - 'body' => [$type => $mapping], + 'type' => $type, + 'body' => [$type => $mapping], ] ); } @@ -98,6 +120,25 @@ public function putMapping($indexName, $type, array $mapping) */ public function deleteByQuery(array $params) { - $this->esClient->deleteByQuery($params); + $this->getClient()->deleteByQuery($params); + } + + /** + * Initialize, if not initialized yet, and return ES client instance + * + * @return \Elasticsearch\Client + * @throws ConnectionDisabledException + */ + private function getClient() + { + if (!$this->config->isEnabled()) { + throw new ConnectionDisabledException(__('ElasticSearch indexer disabled.')); + } + + if (!$this->client instanceof \Elasticsearch\Client) { + $this->client = $this->clientBuilder->build($this->clientConfiguration->getOptions()); + } + + return $this->client; } } diff --git a/src/module-vsbridge-indexer-core/Exception/ConnectionDisabledException.php b/src/module-vsbridge-indexer-core/Exception/ConnectionDisabledException.php new file mode 100644 index 00000000..03cda094 --- /dev/null +++ b/src/module-vsbridge-indexer-core/Exception/ConnectionDisabledException.php @@ -0,0 +1,18 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCore\Exception; + +/** + * Class ConnectionDisabledException + * + * @package \Divante\VsbridgeIndexerCore + */ +class ConnectionDisabledException extends \RuntimeException +{ +} diff --git a/src/module-vsbridge-indexer-core/Index/IndexOperations.php b/src/module-vsbridge-indexer-core/Index/IndexOperations.php index 50c39435..9cf3e8e6 100644 --- a/src/module-vsbridge-indexer-core/Index/IndexOperations.php +++ b/src/module-vsbridge-indexer-core/Index/IndexOperations.php @@ -151,8 +151,11 @@ public function getIndexByName($indexIdentifier, StoreInterface $store) public function getIndexName(StoreInterface $store) { $name = $this->indexSettings->getIndexNamePrefix(); + $storeIdentifier = ('code' === $this->indexSettings->getIndexIdentifier()) + ? $store->getCode() + : $store->getId(); - return $name . '_' . $store->getId(); + return $name . '_' . $storeIdentifier; } /** diff --git a/src/module-vsbridge-indexer-core/Index/IndexSettings.php b/src/module-vsbridge-indexer-core/Index/IndexSettings.php index 60e7731a..510463c7 100644 --- a/src/module-vsbridge-indexer-core/Index/IndexSettings.php +++ b/src/module-vsbridge-indexer-core/Index/IndexSettings.php @@ -80,6 +80,14 @@ public function getIndexNamePrefix() return $this->settingConfig->getIndexNamePrefix(); } + /** + * @return string + */ + public function getIndexIdentifier() + { + return $this->settingConfig->getIndexIdentifier(); + } + /** * @return int */ diff --git a/src/module-vsbridge-indexer-core/Index/Indicies/Config.php b/src/module-vsbridge-indexer-core/Index/Indicies/Config.php index 653a18a6..16be130c 100644 --- a/src/module-vsbridge-indexer-core/Index/Indicies/Config.php +++ b/src/module-vsbridge-indexer-core/Index/Indicies/Config.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-core/Index/Indicies/Config/Converter.php b/src/module-vsbridge-indexer-core/Index/Indicies/Config/Converter.php index 4caca1ac..27e9621b 100644 --- a/src/module-vsbridge-indexer-core/Index/Indicies/Config/Converter.php +++ b/src/module-vsbridge-indexer-core/Index/Indicies/Config/Converter.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-core/Index/Indicies/Config/Reader.php b/src/module-vsbridge-indexer-core/Index/Indicies/Config/Reader.php index 06fe2952..fe90bd5e 100644 --- a/src/module-vsbridge-indexer-core/Index/Indicies/Config/Reader.php +++ b/src/module-vsbridge-indexer-core/Index/Indicies/Config/Reader.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-core/Index/Indicies/Config/SchemaLocator.php b/src/module-vsbridge-indexer-core/Index/Indicies/Config/SchemaLocator.php index fe9f3e1c..ae2724e0 100644 --- a/src/module-vsbridge-indexer-core/Index/Indicies/Config/SchemaLocator.php +++ b/src/module-vsbridge-indexer-core/Index/Indicies/Config/SchemaLocator.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-core/Index/Mapping/GeneralMapping.php b/src/module-vsbridge-indexer-core/Index/Mapping/GeneralMapping.php index e9adc93c..8b34db79 100644 --- a/src/module-vsbridge-indexer-core/Index/Mapping/GeneralMapping.php +++ b/src/module-vsbridge-indexer-core/Index/Mapping/GeneralMapping.php @@ -61,7 +61,6 @@ public function getStockMapping() 'min_qty' => ['type' => FieldInterface::TYPE_DOUBLE], 'min_sale_qty' => ['type' => FieldInterface::TYPE_DOUBLE], 'notify_stock_qty' => ['type' => FieldInterface::TYPE_DOUBLE], - 'product_id' => ['type' => FieldInterface::TYPE_LONG], 'qty' => ['type' => FieldInterface::TYPE_DOUBLE], 'qty_increments' => ['type' => FieldInterface::TYPE_DOUBLE], 'stock_id' => ['type' => FieldInterface::TYPE_LONG], diff --git a/src/module-vsbridge-indexer-core/Indexer/DataProvider/TransactionKey.php b/src/module-vsbridge-indexer-core/Indexer/DataProvider/TransactionKey.php index bffc2a1e..f318729c 100644 --- a/src/module-vsbridge-indexer-core/Indexer/DataProvider/TransactionKey.php +++ b/src/module-vsbridge-indexer-core/Indexer/DataProvider/TransactionKey.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-core/Indexer/GenericIndexerHandler.php b/src/module-vsbridge-indexer-core/Indexer/GenericIndexerHandler.php index 5505ddfe..3a8a6fc1 100644 --- a/src/module-vsbridge-indexer-core/Indexer/GenericIndexerHandler.php +++ b/src/module-vsbridge-indexer-core/Indexer/GenericIndexerHandler.php @@ -18,6 +18,7 @@ use Magento\Framework\Event\ManagerInterface as EventManager; use Magento\Store\Api\Data\StoreInterface; use Psr\Log\LoggerInterface; +use Divante\VsbridgeIndexerCore\Exception\ConnectionDisabledException; /** * Class IndexerHandler @@ -104,6 +105,59 @@ public function __construct( $this->transactionKey = $transactionKey->load(); } + /** + * @param \Traversable $documents + * @param StoreInterface $store + * @param array $requireDataProvides + * + * @return $this + */ + public function updateIndex(\Traversable $documents, StoreInterface $store, array $requireDataProvides) + { + try { + $index = $this->getIndex($store); + $type = $index->getType($this->typeName); + $dataProviders = []; + + foreach ($type->getDataProviders() as $name => $dataProvider) { + if (in_array($name, $requireDataProvides)) { + $dataProviders[] = $dataProvider; + } + } + + if (empty($dataProviders)) { + return $this; + } + + $storeId = (int)$store->getId(); + + foreach ($this->batch->getItems($documents, $this->getBatchSize()) as $docs) { + /** @var \Divante\VsbridgeIndexerCore\Api\DataProviderInterface $datasource */ + foreach ($dataProviders as $datasource) { + if (!empty($docs)) { + $docs = $datasource->addData($docs, $storeId); + } + } + + $docs = $this->convertDataTypes->castFieldsUsingMapping($type, $docs); + + $bulkRequest = $this->indexOperation->createBulk()->updateDocuments( + $index->getName(), + $this->typeName, + $docs + ); + + $response = $this->indexOperation->executeBulk($bulkRequest); + $this->logErrors($response); + $docs = null; + } + + $this->indexOperation->refreshIndex($index); + } catch (ConnectionDisabledException $exception) { + // do nothing, ES indexer disabled in configuration + } + } + /** * @param \Traversable $documents * @param StoreInterface $store @@ -112,37 +166,42 @@ public function __construct( */ public function saveIndex(\Traversable $documents, StoreInterface $store) { - $index = $this->getIndex($store); - $type = $index->getType($this->typeName); + try { + $index = $this->getIndex($store); + $type = $index->getType($this->typeName); + $storeId = (int)$store->getId(); - foreach ($this->batch->getItems($documents, $this->getBatchSize()) as $docs) { - foreach ($type->getDataProviders() as $dataProvider) { - if (!empty($docs)) { - $docs = $dataProvider->addData($docs, (int)$store->getId()); + foreach ($this->batch->getItems($documents, $this->getBatchSize()) as $docs) { + foreach ($type->getDataProviders() as $dataProvider) { + if (!empty($docs)) { + $docs = $dataProvider->addData($docs, $storeId); + } } + + $docs = $this->convertDataTypes->castFieldsUsingMapping($type, $docs); + $bulkRequest = $this->indexOperation->createBulk()->addDocuments( + $index->getName(), + $this->typeName, + $docs + ); + + $response = $this->indexOperation->executeBulk($bulkRequest); + $this->logErrors($response); + $this->eventManager->dispatch( + 'search_engine_save_documents_after', + [ + 'data_type' => $this->typeName, + 'bulk_response' => $response, + ] + ); + + $docs = null; } - $docs = $this->convertDataTypes->castFieldsUsingMapping($type, $docs); - $bulkRequest = $this->indexOperation->createBulk()->addDocuments( - $index->getName(), - $this->typeName, - $docs - ); - - $response = $this->indexOperation->executeBulk($bulkRequest); - $this->logErrors($response); - $this->eventManager->dispatch( - 'search_engine_save_documents_after', - [ - 'data_type' => $this->typeName, - 'bulk_response' => $response, - ] - ); - - $docs = null; + $this->indexOperation->refreshIndex($index); + } catch (ConnectionDisabledException $exception) { + // do nothing, ES indexer disabled in configuration } - - $this->indexOperation->refreshIndex($index); } /** @@ -153,24 +212,28 @@ public function saveIndex(\Traversable $documents, StoreInterface $store) */ public function cleanUpByTransactionKey(StoreInterface $store, array $docIds = null) { - $indexName = $this->indexOperation->getIndexName($store); + try { + $indexName = $this->indexOperation->getIndexName($store); - if ($this->indexOperation->indexExists($indexName)) { - $index = $this->indexOperation->getIndexByName($this->indexIdentifier, $store); - $transactionKeyQuery = ['must_not' => ['term' => ['tsk' => $this->transactionKey]]]; - $query = ['query' => ['bool' => $transactionKeyQuery]]; + if ($this->indexOperation->indexExists($indexName)) { + $index = $this->indexOperation->getIndexByName($this->indexIdentifier, $store); + $transactionKeyQuery = ['must_not' => ['term' => ['tsk' => $this->transactionKey]]]; + $query = ['query' => ['bool' => $transactionKeyQuery]]; - if ($docIds) { - $query['query']['bool']['must']['terms'] = ['_id' => array_values($docIds)]; - } + if ($docIds) { + $query['query']['bool']['must']['terms'] = ['_id' => array_values($docIds)]; + } - $query = [ - 'index' => $index->getName(), - 'type' => $this->typeName, - 'body' => $query, - ]; + $query = [ + 'index' => $index->getName(), + 'type' => $this->typeName, + 'body' => $query, + ]; - $this->indexOperation->deleteByQuery($query); + $this->indexOperation->deleteByQuery($query); + } + } catch (ConnectionDisabledException $exception) { + // do nothing, ES indexer disabled in configuration } } diff --git a/src/module-vsbridge-indexer-core/Indexer/StoreManager.php b/src/module-vsbridge-indexer-core/Indexer/StoreManager.php index 2f10d84b..7f37e017 100644 --- a/src/module-vsbridge-indexer-core/Indexer/StoreManager.php +++ b/src/module-vsbridge-indexer-core/Indexer/StoreManager.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-core/Logger/Handler/Info.php b/src/module-vsbridge-indexer-core/Logger/Handler/Info.php index cd7db115..616e7826 100644 --- a/src/module-vsbridge-indexer-core/Logger/Handler/Info.php +++ b/src/module-vsbridge-indexer-core/Logger/Handler/Info.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-core/Model/Config/Source/IndexIdentifier.php b/src/module-vsbridge-indexer-core/Model/Config/Source/IndexIdentifier.php new file mode 100644 index 00000000..842647c2 --- /dev/null +++ b/src/module-vsbridge-indexer-core/Model/Config/Source/IndexIdentifier.php @@ -0,0 +1,38 @@ + + * @copyright 2019 Divante Sp. z o.o. + * @license See LICENSE_DIVANTE.txt for license details. + */ + +namespace Divante\VsbridgeIndexerCore\Model\Config\Source; + +/** + * Class IndexIdentifier + */ +class IndexIdentifier implements \Magento\Framework\Option\ArrayInterface +{ + /** + * Options getter + * + * @return array + */ + public function toOptionArray() + { + return [ + ['value' => 'id', 'label' => __('Store ID')], + ['value' => 'code', 'label' => __('Store Code')] + ]; + } + + /** + * Get options in "key-value" format + * + * @return array + */ + public function toArray() + { + return ['id' => __('Store ID'), 'code' => __('Store Code')]; + } +} diff --git a/src/module-vsbridge-indexer-core/etc/adminhtml/system.xml b/src/module-vsbridge-indexer-core/etc/adminhtml/system.xml index 028518b3..2db63717 100644 --- a/src/module-vsbridge-indexer-core/etc/adminhtml/system.xml +++ b/src/module-vsbridge-indexer-core/etc/adminhtml/system.xml @@ -14,15 +14,26 @@ + + + Enable to run indexes with Elasticsearch. + Magento\Config\Model\Config\Source\Yesno + For every store view separated index is created in Elastic Magento\Config\Model\Config\Source\Store + + 1 + + + 1 + @@ -45,23 +56,32 @@ 1 - + 1 + Magento\Config\Model\Config\Backend\Encrypted + + 1 + required-entry validate-digits - For each store view, store id will be added to index name, e.g. "vue_storefront_magento_1" + For each store view, store id or code will be added to index name, e.g. "vue_storefront_magento_1" + + + + Choose if the index will append the Store ID or Store Code to the index name, e.g. "vue_storefront_magento_1" or "vue_storefront_magento_default" + Divante\VsbridgeIndexerCore\Model\Config\Source\IndexIdentifier @@ -72,6 +92,9 @@ + + 1 + Magento\Config\Model\Config\Source\Yesno diff --git a/src/module-vsbridge-indexer-core/etc/config.xml b/src/module-vsbridge-indexer-core/etc/config.xml index c3d6f81c..e7108e04 100644 --- a/src/module-vsbridge-indexer-core/etc/config.xml +++ b/src/module-vsbridge-indexer-core/etc/config.xml @@ -13,15 +13,18 @@ 127.0.0.1 9200 + 1000 vue_storefront_magento + id 1000 0 1 + 0 0 diff --git a/src/module-vsbridge-indexer-core/etc/di.xml b/src/module-vsbridge-indexer-core/etc/di.xml index 87364503..8f3dd332 100644 --- a/src/module-vsbridge-indexer-core/etc/di.xml +++ b/src/module-vsbridge-indexer-core/etc/di.xml @@ -50,4 +50,21 @@ Divante\VsbridgeIndexerCore\Cache\Logger\CacheLogger + + + + + + 1 + + + + + + + + Divante\VsbridgeIndexerCore\Console\Command\RebuildEsIndexCommand + + + diff --git a/src/module-vsbridge-indexer-review/Index/Mapping/Review.php b/src/module-vsbridge-indexer-review/Index/Mapping/Review.php index 4b1a8e73..1de11822 100644 --- a/src/module-vsbridge-indexer-review/Index/Mapping/Review.php +++ b/src/module-vsbridge-indexer-review/Index/Mapping/Review.php @@ -25,11 +25,6 @@ class Review implements MappingInterface */ private $eventManager; - /** - * @var string - */ - private $type; - /** * CmsBlock constructor. * @@ -40,22 +35,6 @@ public function __construct(EventManager $eventManager) $this->eventManager = $eventManager; } - /** - * @inheritdoc - */ - public function setType(string $type) - { - $this->type = $type; - } - - /** - * @return string - */ - public function getType() - { - return $this->type; - } - /** * @inheritdoc */ diff --git a/src/module-vsbridge-indexer-tax/Index/Mapping/Tax.php b/src/module-vsbridge-indexer-tax/Index/Mapping/Tax.php index cee864a0..4e731395 100644 --- a/src/module-vsbridge-indexer-tax/Index/Mapping/Tax.php +++ b/src/module-vsbridge-indexer-tax/Index/Mapping/Tax.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. @@ -14,7 +14,6 @@ /** * Class Tax - * @package Divante\VsbridgeIndexerTax\Index\Mapping */ class Tax implements MappingInterface { @@ -24,11 +23,6 @@ class Tax implements MappingInterface */ private $eventManager; - /** - * @var string - */ - private $type; - /** * CmsBlock constructor. * @@ -39,22 +33,6 @@ public function __construct(EventManager $eventManager) $this->eventManager = $eventManager; } - /** - * @inheritdoc - */ - public function setType(string $type) - { - $this->type = $type; - } - - /** - * @return string - */ - public function getType() - { - return $this->type; - } - /** * @inheritdoc */ diff --git a/src/module-vsbridge-indexer-tax/Model/Indexer/Action/TaxRule.php b/src/module-vsbridge-indexer-tax/Model/Indexer/Action/TaxRule.php index 67c08c57..80e9fd40 100644 --- a/src/module-vsbridge-indexer-tax/Model/Indexer/Action/TaxRule.php +++ b/src/module-vsbridge-indexer-tax/Model/Indexer/Action/TaxRule.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-tax/Model/Indexer/DataProvider/TaxClasses.php b/src/module-vsbridge-indexer-tax/Model/Indexer/DataProvider/TaxClasses.php index 387969fb..b8bfc239 100644 --- a/src/module-vsbridge-indexer-tax/Model/Indexer/DataProvider/TaxClasses.php +++ b/src/module-vsbridge-indexer-tax/Model/Indexer/DataProvider/TaxClasses.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-tax/Model/Indexer/DataProvider/TaxRates.php b/src/module-vsbridge-indexer-tax/Model/Indexer/DataProvider/TaxRates.php index 55dcd783..dcbbdc37 100644 --- a/src/module-vsbridge-indexer-tax/Model/Indexer/DataProvider/TaxRates.php +++ b/src/module-vsbridge-indexer-tax/Model/Indexer/DataProvider/TaxRates.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-tax/Model/Indexer/TaxRules.php b/src/module-vsbridge-indexer-tax/Model/Indexer/TaxRules.php index f08da4e3..2159fa30 100644 --- a/src/module-vsbridge-indexer-tax/Model/Indexer/TaxRules.php +++ b/src/module-vsbridge-indexer-tax/Model/Indexer/TaxRules.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-tax/ResourceModel/Rates.php b/src/module-vsbridge-indexer-tax/ResourceModel/Rates.php index e96b71ba..6e9839a1 100644 --- a/src/module-vsbridge-indexer-tax/ResourceModel/Rates.php +++ b/src/module-vsbridge-indexer-tax/ResourceModel/Rates.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-tax/ResourceModel/Rules.php b/src/module-vsbridge-indexer-tax/ResourceModel/Rules.php index c7f64036..59f98b74 100644 --- a/src/module-vsbridge-indexer-tax/ResourceModel/Rules.php +++ b/src/module-vsbridge-indexer-tax/ResourceModel/Rules.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details. diff --git a/src/module-vsbridge-indexer-tax/ResourceModel/TaxClasses.php b/src/module-vsbridge-indexer-tax/ResourceModel/TaxClasses.php index 0d32dfd0..d5ed75b7 100644 --- a/src/module-vsbridge-indexer-tax/ResourceModel/TaxClasses.php +++ b/src/module-vsbridge-indexer-tax/ResourceModel/TaxClasses.php @@ -1,6 +1,6 @@ * @copyright 2019 Divante Sp. z o.o. * @license See LICENSE_DIVANTE.txt for license details.