From 1bf2d79375ef48c9a6c2dc265c1604f0c9a699e9 Mon Sep 17 00:00:00 2001 From: Agata Firlejczyk Date: Wed, 10 Jul 2019 22:38:47 +0200 Subject: [PATCH] Add support for configurable value label. #81 --- CHANGELOG.MD | 23 ++ .../Index/Mapping/Product.php | 218 +++++++++++------- .../Model/Attribute/LoadOptionLabelById.php | 79 +++++++ .../DataProvider/Product/ConfigurableData.php | 20 +- 4 files changed, 252 insertions(+), 88 deletions(-) create mode 100644 src/module-vsbridge-indexer-catalog/Model/Attribute/LoadOptionLabelById.php diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 71a87ac5..1e18f86b 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -7,6 +7,7 @@ - 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 @@ -24,6 +25,28 @@ category will be visible in menu sidebar. - 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_". ```php Description: diff --git a/src/module-vsbridge-indexer-catalog/Index/Mapping/Product.php b/src/module-vsbridge-indexer-catalog/Index/Mapping/Product.php index 21f1785a..7de16585 100644 --- a/src/module-vsbridge-indexer-catalog/Index/Mapping/Product.php +++ b/src/module-vsbridge-indexer-catalog/Index/Mapping/Product.php @@ -165,104 +165,152 @@ 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], - ], - ], - ], + '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], ], - '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], + ]; + } + + /** + * @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], + ], ], ], - '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 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, + ] ], ], ], - 'category' => [ - '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, - ] - ], + ]; + } + + /** + * @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], ], ], - ], - '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 getCustomOptionsMapping(): array + { + 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], ] ] - ], - '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] - ], + ] + ]; + } + + /** + * @return array + */ + private function getTierPricesMapping(): array + { + 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/Indexer/DataProvider/Product/ConfigurableData.php b/src/module-vsbridge-indexer-catalog/Model/Indexer/DataProvider/Product/ConfigurableData.php index 28e43c81..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,6 +8,7 @@ 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; @@ -74,6 +75,11 @@ class ConfigurableData implements DataProviderInterface */ private $tierPriceProcessor; + /** + * @var LoadOptionLabelById + */ + private $loadOptionLabelById; + /** * ConfigurableData constructor. * @@ -81,6 +87,7 @@ class ConfigurableData implements DataProviderInterface * @param ConfigurableResource $configurableResource * @param AttributeDataProvider $attributeResource * @param LoadInventoryInterface $loadInventory + * @param LoadOptionLabelById $loadOptionLabelById * @param ConfigurableAttributes $configurableAttributes * @param TierPriceProcessor $tierPriceProcessor * @param InventoryProcessor $inventoryProcessor @@ -90,6 +97,7 @@ public function __construct( ConfigurableResource $configurableResource, AttributeDataProvider $attributeResource, LoadInventoryInterface $loadInventory, + LoadOptionLabelById $loadOptionLabelById, ConfigurableAttributes $configurableAttributes, TierPriceProcessor $tierPriceProcessor, InventoryProcessor $inventoryProcessor @@ -100,6 +108,7 @@ public function __construct( $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); } @@ -198,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 = @@ -231,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;