From ad5d3e5971e83059f559184ec371f86db8371b96 Mon Sep 17 00:00:00 2001 From: Richard BAYET Date: Wed, 18 Oct 2023 19:37:27 +0200 Subject: [PATCH] Ability to create rules based on stock qty and searchable content --- .../Product/SpecialAttribute/Search.php | 231 ++++++++++++++++++ .../Product/SpecialAttribute/StockQty.php | 107 ++++++++ .../etc/di.xml | 2 + .../i18n/en_US.csv | 2 + .../i18n/fr_FR.csv | 2 + 5 files changed, 344 insertions(+) create mode 100644 src/module-elasticsuite-catalog-rule/Model/Rule/Condition/Product/SpecialAttribute/Search.php create mode 100644 src/module-elasticsuite-catalog-rule/Model/Rule/Condition/Product/SpecialAttribute/StockQty.php diff --git a/src/module-elasticsuite-catalog-rule/Model/Rule/Condition/Product/SpecialAttribute/Search.php b/src/module-elasticsuite-catalog-rule/Model/Rule/Condition/Product/SpecialAttribute/Search.php new file mode 100644 index 000000000..2877ac503 --- /dev/null +++ b/src/module-elasticsuite-catalog-rule/Model/Rule/Condition/Product/SpecialAttribute/Search.php @@ -0,0 +1,231 @@ + + * @copyright 2021 Smile + * @license Open Software License ("OSL") v. 3.0 + */ + +namespace Smile\ElasticsuiteCatalogRule\Model\Rule\Condition\Product\SpecialAttribute; + +use Smile\ElasticsuiteCatalogRule\Api\Rule\Condition\Product\SpecialAttributeInterface; +use Smile\ElasticsuiteCatalogRule\Model\Rule\Condition\Product as ProductCondition; +use Smile\ElasticsuiteCore\Api\Search\ContextInterface; +use Smile\ElasticsuiteCore\Api\Search\Request\ContainerConfigurationInterface; +use Smile\ElasticsuiteCore\Api\Search\Spellchecker\RequestInterfaceFactory as SpellcheckRequestFactory; +use Smile\ElasticsuiteCore\Api\Search\SpellcheckerInterface; +use Smile\ElasticsuiteCore\Search\Request\ContainerConfigurationFactory; +use Smile\ElasticsuiteCore\Search\Request\Query\Builder as QueryBuilder; +use Smile\ElasticsuiteCore\Search\Request\Query\QueryFactory; +use Smile\ElasticsuiteCore\Search\Request\QueryInterface; + +/** + * Class Search + * + * @category Elasticsuite + * @package Elasticsuite\CatalogRule + * @author Richard Bayet + */ +class Search implements SpecialAttributeInterface +{ + /** + * @var ContainerConfigurationFactory + */ + private $containerConfigFactory; + + /** + * @var ContextInterface + */ + private $searchContext; + + /** + * @var QueryBuilder + */ + private $queryBuilder; + + /** + * @var QueryFactory + */ + private $queryFactory; + + /** + * @var SpellcheckRequestFactory + */ + private $spellcheckRequestFactory; + + /** + * @var SpellcheckerInterface + */ + private $spellchecker; + + /** + * Search constructor. + * + * @param ContainerConfigurationFactory $containerConfigFactory Container configuration factory. + * @param ContextInterface $searchContext Search context. + * @param QueryBuilder $queryBuilder Query builder. + * @param QueryFactory $queryFactory Query factory. + * @param SpellcheckRequestFactory $spellcheckRequestFactory Spellcheck request factory. + * @param SpellcheckerInterface $spellchecker Spellchecker. + */ + public function __construct( + ContainerConfigurationFactory $containerConfigFactory, + ContextInterface $searchContext, + QueryBuilder $queryBuilder, + QueryFactory $queryFactory, + SpellcheckRequestFactory $spellcheckRequestFactory, + SpellcheckerInterface $spellchecker + ) { + $this->containerConfigFactory = $containerConfigFactory; + $this->searchContext = $searchContext; + $this->queryBuilder = $queryBuilder; + $this->queryFactory = $queryFactory; + $this->spellcheckRequestFactory = $spellcheckRequestFactory; + $this->spellchecker = $spellchecker; + } + + /** + * {@inheritdoc} + */ + public function getAttributeCode() + { + return 'search'; + } + + /** + * {@inheritdoc} + */ + public function getSearchQuery(ProductCondition $condition) + { + $containerConfiguration = $this->getContainerConfiguration(); + $queryText = $condition->getValue(); + $searchQuery = $this->getFullTextQuery($containerConfiguration, $queryText); + + if (substr($condition->getOperator(), 0, 1) === '!') { + $searchQuery = $this->queryFactory->create(QueryInterface::TYPE_NOT, ['query' => $searchQuery]); + } + + return $searchQuery; + } + + /** + * {@inheritdoc} + */ + public function getOperatorName() + { + return null; + } + + /** + * {@inheritdoc} + */ + public function getInputType() + { + return 'string'; + } + + /** + * {@inheritdoc} + */ + public function getValueElementType() + { + return 'text'; + } + + /** + * {@inheritdoc} + */ + public function getValueName($value) + { + if ($value === null || '' === $value) { + return '...'; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getValue($value) + { + return $value; + } + + /** + * {@inheritdoc} + */ + public function getValueOptions() + { + return []; + } + + /** + * {@inheritdoc} + */ + public function getLabel() + { + return __('Searchable content'); + } + + /** + * Get fulltext container configuration. + * + * @return ContainerConfigurationInterface + */ + private function getContainerConfiguration() + { + return $this->containerConfigFactory->create( + ['containerName' => 'quick_search_container', 'storeId' => (int) $this->searchContext->getStoreId()] + ); + } + + /** + * Retrieve fulltext search query for a given query text + * + * @param ContainerConfigurationInterface $containerConfiguration Container configuration. + * @param string $queryText The query text. + * + * @return QueryInterface + */ + private function getFullTextQuery(ContainerConfigurationInterface $containerConfiguration, $queryText) + { + $spellingType = $this->getSpellingType($containerConfiguration, $queryText); + + return $this->queryBuilder->createFulltextQuery($containerConfiguration, $queryText, $spellingType); + } + + /** + * Retrieve the spelling type for a fulltext query. + * + * @param ContainerConfigurationInterface $containerConfiguration Container configuration. + * @param string $queryText Query text. + * + * @return int + */ + private function getSpellingType(ContainerConfigurationInterface $containerConfiguration, $queryText) + { + if (is_array($queryText)) { + $queryText = implode(" ", $queryText); + } + + $spellcheckRequestParams = [ + 'index' => $containerConfiguration->getIndexName(), + 'queryText' => $queryText, + 'cutoffFrequency' => $containerConfiguration->getRelevanceConfig()->getCutOffFrequency(), + 'isUsingAllTokens' => $containerConfiguration->getRelevanceConfig()->isUsingAllTokens(), + 'isUsingReference' => $containerConfiguration->getRelevanceConfig()->isUsingReferenceAnalyzer(), + 'isUsingEdgeNgram' => $containerConfiguration->getRelevanceConfig()->isUsingEdgeNgramAnalyzer(), + ]; + + $spellcheckRequest = $this->spellcheckRequestFactory->create($spellcheckRequestParams); + + return $this->spellchecker->getSpellingType($spellcheckRequest); + } +} diff --git a/src/module-elasticsuite-catalog-rule/Model/Rule/Condition/Product/SpecialAttribute/StockQty.php b/src/module-elasticsuite-catalog-rule/Model/Rule/Condition/Product/SpecialAttribute/StockQty.php new file mode 100644 index 000000000..07d849306 --- /dev/null +++ b/src/module-elasticsuite-catalog-rule/Model/Rule/Condition/Product/SpecialAttribute/StockQty.php @@ -0,0 +1,107 @@ + + * @copyright 2021 Smile + * @license Open Software License ("OSL") v. 3.0 + */ + +namespace Smile\ElasticsuiteCatalogRule\Model\Rule\Condition\Product\SpecialAttribute; + +use Smile\ElasticsuiteCatalogRule\Api\Rule\Condition\Product\SpecialAttributeInterface; +use Smile\ElasticsuiteCatalogRule\Model\Rule\Condition\Product as ProductCondition; + +/** + * Class StockQty + * + * @category Elasticsuite + * @package Elasticsuite\CatalogRule + * @author Richard Bayet + */ +class StockQty implements SpecialAttributeInterface +{ + /** + * {@inheritdoc} + */ + public function getAttributeCode() + { + return 'stock.qty'; + } + + /** + * {@inheritdoc} + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function getSearchQuery(ProductCondition $condition) + { + // Query can be computed directly with the attribute code and value. (eg. stock.qty < 5). + return null; + } + + /** + * {@inheritdoc} + */ + public function getOperatorName() + { + return null; + } + + /** + * {@inheritdoc} + */ + public function getInputType() + { + return 'numeric'; + } + + /** + * {@inheritdoc} + */ + public function getValueElementType() + { + return 'text'; + } + + /** + * {@inheritdoc} + */ + public function getValueName($value) + { + if ($value === null || '' === $value) { + return '...'; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getValue($rawValue) + { + return $rawValue; + } + + /** + * {@inheritdoc} + */ + public function getValueOptions() + { + return []; + } + + /** + * {@inheritdoc} + */ + public function getLabel() + { + return __('Stock qty'); + } +} diff --git a/src/module-elasticsuite-catalog-rule/etc/di.xml b/src/module-elasticsuite-catalog-rule/etc/di.xml index 4cc808cbd..f0158c88d 100644 --- a/src/module-elasticsuite-catalog-rule/etc/di.xml +++ b/src/module-elasticsuite-catalog-rule/etc/di.xml @@ -30,6 +30,8 @@ isGiftCardProduct isGroupedProduct isSimpleProduct + Smile\ElasticsuiteCatalogRule\Model\Rule\Condition\Product\SpecialAttribute\StockQty + Smile\ElasticsuiteCatalogRule\Model\Rule\Condition\Product\SpecialAttribute\Search diff --git a/src/module-elasticsuite-catalog-rule/i18n/en_US.csv b/src/module-elasticsuite-catalog-rule/i18n/en_US.csv index ce48ca1c1..3c33d7747 100644 --- a/src/module-elasticsuite-catalog-rule/i18n/en_US.csv +++ b/src/module-elasticsuite-catalog-rule/i18n/en_US.csv @@ -12,3 +12,5 @@ "Catalog Rules Configuration","Catalog Rules Configuration" "Alphabetical sorting of attribute options in the rule engine","Alphabetical sorting of attribute options in the rule engine" "If enabled, will forcefully sort select and multiselect attributes' option labels alphabetically when displaying them in the rule engine used by Optimizers and Virtual Categories. Useful if you have product attributes with very long list of options.","If enabled, will forcefully sort select and multiselect attributes' option labels alphabetically when displaying them in the rule engine used by Optimizers and Virtual Categories. Useful if you have product attributes with very long list of options." +"Stock qty","Stock qty" +"Searchable content","Searchable content" diff --git a/src/module-elasticsuite-catalog-rule/i18n/fr_FR.csv b/src/module-elasticsuite-catalog-rule/i18n/fr_FR.csv index 22f8c598c..aef16635f 100644 --- a/src/module-elasticsuite-catalog-rule/i18n/fr_FR.csv +++ b/src/module-elasticsuite-catalog-rule/i18n/fr_FR.csv @@ -12,3 +12,5 @@ "Catalog Rules Configuration","Configuration des règles catalogue" "Alphabetical sorting of attribute options in the rule engine","Tri alphabétique des options d'attributs dans le moteur de règles" "If enabled, will forcefully sort select and multiselect attributes' option labels alphabetically when displaying them in the rule engine used by Optimizers and Virtual Categories. Useful if you have product attributes with very long list of options.","Si activé, forcera un tri alphabétique des labels des options d'attributs select et multiselect lors de leur affichage dans le moteur de règles utilisé par les Optimiseurs et les Catégories Virtuelles. Utile si vous avez des attributs produits avec un très grand nombre d'options." +"Stock qty","Quantité en stock" +"Searchable content","Contenu recherchable"