diff --git a/src/module-elasticsuite-catalog/Block/Plugin/Adminhtml/Product/Attribute/Edit/Tab/FrontPlugin.php b/src/module-elasticsuite-catalog/Block/Plugin/Adminhtml/Product/Attribute/Edit/Tab/FrontPlugin.php index bc29da6ee..958f94e5b 100644 --- a/src/module-elasticsuite-catalog/Block/Plugin/Adminhtml/Product/Attribute/Edit/Tab/FrontPlugin.php +++ b/src/module-elasticsuite-catalog/Block/Plugin/Adminhtml/Product/Attribute/Edit/Tab/FrontPlugin.php @@ -19,6 +19,7 @@ use Magento\Framework\Data\Form; use Magento\Framework\Registry; use Smile\ElasticsuiteCatalog\Model\Attribute\Source\FilterSortOrder; +use Smile\ElasticsuiteCore\Api\Index\Mapping\FieldInterface; use Smile\ElasticsuiteCore\Search\Request\BucketInterface; use Magento\Framework\Data\Form\Element\Fieldset; use Magento\Catalog\Api\Data\EavAttributeInterface; @@ -105,6 +106,16 @@ public function afterSetForm(Front $subject, Front $result, Form $form) $form->getElement('is_searchable')->setDisabled(1); } + if (($this->getAttribute()->getSourceModel() == 'Magento\Eav\Model\Entity\Attribute\Source\Boolean') + || ($this->getAttribute()->getBackendType() == 'int') + || ($this->getAttribute()->getFrontendClass() == 'validate-digits') + || ($this->getAttribute()->getBackendType() == 'decimal' || $this->getAttribute()->getFrontendClass() == 'validate-number') + || (in_array($this->getAttribute()->getFrontendInput(), ['select', 'multiselect']) + || $this->getAttribute()->getSourceModel() != '') + ) { + $this->addIncludeZeroFalseField($fieldset); + } + $this->appendFieldsDependency($subject); return $result; @@ -399,6 +410,30 @@ private function appendSliderDisplayRelatedFields($form, $subject) return $this; } + /** + * Add field allowing to configure if zero/false values should be indexed or ignored. + * + * @param Fieldset $fieldset Target fieldset + * + * @return FrontPlugin + */ + private function addIncludeZeroFalseField(Fieldset $fieldset) + { + $fieldset->addField( + 'include_zero_false_values', + 'select', + [ + 'name' => 'include_zero_false_values', + 'label' => __('Include zero or false values'), + 'values' => $this->booleanSource->toOptionArray(), + 'note' => __( + 'If set to Yes, zero (integer or numeric attribute) or false (boolean attribute) values will be indexed in the search engine (default is No). Also applies to source model keys/values of Dropdown/Multiple Select attributes.' + ), + ], + 'used_for_sortby' + ); + } + /** * Manage dependency between fields. * diff --git a/src/module-elasticsuite-catalog/Helper/AbstractAttribute.php b/src/module-elasticsuite-catalog/Helper/AbstractAttribute.php index 34aac5940..a07fb7bea 100644 --- a/src/module-elasticsuite-catalog/Helper/AbstractAttribute.php +++ b/src/module-elasticsuite-catalog/Helper/AbstractAttribute.php @@ -64,6 +64,11 @@ abstract class AbstractAttribute extends Mapping */ private $attributeMappers = []; + /** + * @var array + */ + private $attributeCleaners = []; + /** * @param Context $context Helper context. * @param AttributeFactory $attributeFactory Factory used to create attributes. @@ -162,6 +167,8 @@ public function getFieldType(AttributeInterface $attribute) * multivalued attributes merging on composite products). * ES doesn't care of having array of int when it an int is required. * + * @SuppressWarnings(PHPMD.ElseExpression) + * * @param AttributeInterface $attribute Product attribute. * @param integer $storeId Store id. * @param mixed $value Raw value to be parsed. @@ -174,13 +181,26 @@ public function prepareIndexValue(AttributeInterface $attribute, $storeId, $valu $values = []; $mapperKey = 'simple_' . $attribute->getId(); - if (!isset($this->attributeMappers[$mapperKey])) { $this->attributeMappers[$mapperKey] = function ($value) use ($attribute) { return $this->prepareSimpleIndexAttributeValue($attribute, $value); }; } + if (!isset($this->attributeCleaners[$mapperKey])) { + if ($attribute->getIncludeZeroFalseValues()) { + // Filter empty values while keeping "0" (int or float) and "false" value. + $this->attributeCleaners[$mapperKey] = function ($value) { + return (($value === false) || strlen($value)); + }; + } else { + // Filter out empty values. Also removes "0" and "false" values. + $this->attributeCleaners[$mapperKey] = function ($value) { + return !empty($value); + }; + } + } + if ($this->usesSource($attribute) && !is_array($value)) { $value = explode(',', $value); } @@ -190,8 +210,7 @@ public function prepareIndexValue(AttributeInterface $attribute, $storeId, $valu } $value = array_map($this->attributeMappers[$mapperKey], $value); - // Filter empty values while keeping "0" (int or float) and "false" value. - $value = array_filter($value, function ($v) { return (($v === false) || strlen($v)); }); + $value = array_filter($value, $this->attributeCleaners[$mapperKey]); $value = array_values($value); $values[$attributeCode] = $value; diff --git a/src/module-elasticsuite-catalog/Model/Layer/Filter/Boolean.php b/src/module-elasticsuite-catalog/Model/Layer/Filter/Boolean.php index ddae683d4..1e272f920 100644 --- a/src/module-elasticsuite-catalog/Model/Layer/Filter/Boolean.php +++ b/src/module-elasticsuite-catalog/Model/Layer/Filter/Boolean.php @@ -22,6 +22,7 @@ use Smile\ElasticsuiteCatalog\Api\LayeredNavAttributeInterface; use Smile\ElasticsuiteCatalog\Helper\ProductAttribute; use Smile\ElasticsuiteCatalog\Model\Attribute\LayeredNavAttributesProvider; +use Smile\ElasticsuiteCore\Search\Request\BucketInterface; /** * Product boolean filter implementation. @@ -184,6 +185,12 @@ protected function _initItems() $item->setApplyFilterValue(array_values($applyValue)); } + if (($this->getAttributeModel()->getFacetSortOrder() == BucketInterface::SORT_ORDER_MANUAL) + && (count($this->_items) > 1) + ) { + krsort($this->_items, SORT_NUMERIC); + } + return $this; } } diff --git a/src/module-elasticsuite-catalog/Plugin/Catalog/Eav/AttributePlugin.php b/src/module-elasticsuite-catalog/Plugin/Catalog/Eav/AttributePlugin.php index b90be3343..ca3314695 100644 --- a/src/module-elasticsuite-catalog/Plugin/Catalog/Eav/AttributePlugin.php +++ b/src/module-elasticsuite-catalog/Plugin/Catalog/Eav/AttributePlugin.php @@ -27,14 +27,22 @@ class AttributePlugin */ private $indicesConfig; + /** + * @var \Magento\Framework\Indexer\IndexerRegistry + */ + private $indexerRegistry; + /** * AttributePlugin constructor. * * @param \Smile\ElasticsuiteCore\Index\Indices\Config $indicesConfig Indices config. */ - public function __construct(\Smile\ElasticsuiteCore\Index\Indices\Config $indicesConfig) - { + public function __construct( + \Smile\ElasticsuiteCore\Index\Indices\Config $indicesConfig, + \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry + ) { $this->indicesConfig = $indicesConfig; + $this->indexerRegistry = $indexerRegistry; } /** @@ -50,15 +58,24 @@ public function afterSave( \Magento\Catalog\Api\Data\ProductAttributeInterface $result ) { $cleanCache = false; + $needReindex = false; if ($subject->dataHasChangedFor('search_weight')) { $cleanCache = true; } + if ($subject->dataHasChangedFor('include_zero_false_values')) { + $needReindex = true; + } + if ($cleanCache) { $this->indicesConfig->reset(); } + if ($needReindex) { + $this->indexerRegistry->get(\Magento\CatalogSearch\Model\Indexer\Fulltext::INDEXER_ID)->invalidate(); + } + return $result; } } diff --git a/src/module-elasticsuite-catalog/Setup/CatalogSetup.php b/src/module-elasticsuite-catalog/Setup/CatalogSetup.php index d6cb35361..c18d76ce3 100644 --- a/src/module-elasticsuite-catalog/Setup/CatalogSetup.php +++ b/src/module-elasticsuite-catalog/Setup/CatalogSetup.php @@ -550,6 +550,32 @@ public function addSortOrderMissingFields(SchemaSetupInterface $setup) ); } + /** + * Add "include_zero_false_values" field to catalog_eav_attribute table. + * + * @param \Magento\Framework\Setup\SchemaSetupInterface $setup Schema Setup + * + * @return void + */ + public function addIncludeZeroFalseValues(SchemaSetupInterface $setup) + { + $connection = $setup->getConnection(); + $table = $setup->getTable('catalog_eav_attribute'); + + // Append a column 'include_zero_false_values' into the db. + $connection->addColumn( + $table, + 'include_zero_false_values', + [ + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_BOOLEAN, + 'nullable' => false, + 'default' => 0, + 'size' => 1, + 'comment' => 'Should the search engine index zero (integer or decimal attribute) or false (boolean attribute) values', + ] + ); + } + /** * Update attribute value for an entity with a default value. * All existing values are erased by the new value. diff --git a/src/module-elasticsuite-catalog/Setup/InstallSchema.php b/src/module-elasticsuite-catalog/Setup/InstallSchema.php index 33ee86bcb..b69fa3afb 100644 --- a/src/module-elasticsuite-catalog/Setup/InstallSchema.php +++ b/src/module-elasticsuite-catalog/Setup/InstallSchema.php @@ -72,6 +72,9 @@ public function install(SchemaSetupInterface $setup, ModuleContextInterface $con // Introduced in version 1.5.1. $this->catalogSetup->addSortOrderMissingFields($setup); + // Introduced in version 1.5.2. + $this->catalogSetup->addIncludeZeroFalseValues($setup); + $setup->endSetup(); } } diff --git a/src/module-elasticsuite-catalog/Setup/UpgradeSchema.php b/src/module-elasticsuite-catalog/Setup/UpgradeSchema.php index 7e310f728..74e2edc7d 100644 --- a/src/module-elasticsuite-catalog/Setup/UpgradeSchema.php +++ b/src/module-elasticsuite-catalog/Setup/UpgradeSchema.php @@ -80,6 +80,10 @@ public function upgrade( $this->catalogSetup->addSortOrderMissingFields($setup); } + if (version_compare($context->getVersion(), '1.5.2', '<')) { + $this->catalogSetup->addIncludeZeroFalseValues($setup); + } + $setup->endSetup(); } } diff --git a/src/module-elasticsuite-catalog/etc/module.xml b/src/module-elasticsuite-catalog/etc/module.xml index b8741784b..2911361e2 100644 --- a/src/module-elasticsuite-catalog/etc/module.xml +++ b/src/module-elasticsuite-catalog/etc/module.xml @@ -16,7 +16,7 @@ */ --> - + diff --git a/src/module-elasticsuite-catalog/i18n/en_US.csv b/src/module-elasticsuite-catalog/i18n/en_US.csv index 4eeebcf88..43f4f4e8e 100755 --- a/src/module-elasticsuite-catalog/i18n/en_US.csv +++ b/src/module-elasticsuite-catalog/i18n/en_US.csv @@ -102,3 +102,5 @@ Attributes,Attributes "No value matching the search %1.","No value matching the search %1." "Enable adaptive slider","Enable adaptive slider" "If enabled, when necessary to support the presence of outlier values in the navigation context (for instance, a very high price amidst a majority of low prices), the price slider behavior changes so that the middle of the slider range corresponds to the median price instead of the price at the middle of the range.","If enabled, when necessary to support the presence of outlier values in the navigation context (for instance, a very high price amidst a majority of low prices), the price slider behavior changes so that the middle of the slider range corresponds to the median price instead of the price at the middle of the range." +"Include zero or false values","Include zero or false values" +"If set to Yes, zero (integer or numeric attribute) or false (boolean attribute) values will be indexed in the search engine (default is No). Also applies to source model keys/values of Dropdown/Multiple Select attributes.","If set to Yes, zero (integer or numeric attribute) or false (boolean attribute) values will be indexed in the search engine (default is No). Also applies to source model keys/values of Dropdown/Multiple Select attributes." diff --git a/src/module-elasticsuite-catalog/i18n/fr_FR.csv b/src/module-elasticsuite-catalog/i18n/fr_FR.csv index fde7288ca..00738f027 100755 --- a/src/module-elasticsuite-catalog/i18n/fr_FR.csv +++ b/src/module-elasticsuite-catalog/i18n/fr_FR.csv @@ -102,3 +102,5 @@ Attributes,Attributs "No value matching the search %1.","Aucun résultat pour la recherche %s." "Enable adaptive slider","Activer le slider adaptatif" "If enabled, when necessary to support the presence of outlier values in the navigation context (for instance, a very high price amidst a majority of low prices), the price slider behavior changes so that the middle of the slider range corresponds to the median price instead of the price at the middle of the range.","Si activé, lorsque c'est nécessaire pour supporter la présence de valeurs extrêmes dans le contexte de navigation (par exemple, un prix très élevé au milieu d'une majorité de prix bas), le comportement du slider de prix change de façon à ce que le milieu de la plage du slider corresponde au prix médian au lieu du prix situé au milieu de la plage de prix." +"Include zero or false values","Inclure les valeurs à zéro ou 'false'" +"If set to Yes, zero (integer or numeric attribute) or false (boolean attribute) values will be indexed in the search engine (default is No). Also applies to source model keys/values of Dropdown/Multiple Select attributes.","Si réglé à Oui, les valeurs à zéro (attribute entier ou numérique) ou 'false/Non' (attribut booléen) seront indexées dans le moteur de recherche (ce n'est pas le cas par défaut). S'applique également aux clefs/valeurs des modèles sources des attributs Dropdown/Multiple Select."