From ed074d3b3507814de96f927689f14be3cc3329e4 Mon Sep 17 00:00:00 2001 From: Pierre Gauthier Date: Tue, 12 Nov 2024 15:20:46 +0100 Subject: [PATCH] Improve search request building --- src/Engine/ExpressionVisitor.php | 96 ------------ src/Engine/SearchEngine.php | 2 +- src/Indexer/IndexDataProvider.php | 4 +- .../Normalizer/BooleanDataNormalizer.php | 2 +- .../Normalizer/CategoryDataNormalizer.php | 12 +- .../Normalizer/VisibilityDataNormalizer.php | 20 ++- src/Indexer/Provider/CatalogProvider.php | 33 ++-- src/Indexer/Provider/SourceFieldProvider.php | 16 +- src/RequestBuilder/GallyRequestBuilder.php | 145 ------------------ src/Resources/config/oro/website_search.yml | 12 +- src/Resources/config/services/search.yml | 24 ++- src/Search/ContextProvider.php | 1 - src/Search/ExpressionVisitor.php | 87 +++++++---- .../Extension/GallyDataGridExtension.php | 19 ++- src/{ => Search/Filter}/Form/ChoiceLoader.php | 2 +- .../Filter}/Form/SelectFormFilter.php | 2 +- src/{ => Search}/Filter/SelectFilter.php | 4 +- src/Search/GallyRequestBuilder.php | 8 +- 18 files changed, 162 insertions(+), 327 deletions(-) delete mode 100644 src/Engine/ExpressionVisitor.php delete mode 100644 src/RequestBuilder/GallyRequestBuilder.php rename src/{ => Search}/Extension/GallyDataGridExtension.php (96%) rename src/{ => Search/Filter}/Form/ChoiceLoader.php (96%) rename src/{ => Search/Filter}/Form/SelectFormFilter.php (98%) rename src/{ => Search}/Filter/SelectFilter.php (95%) diff --git a/src/Engine/ExpressionVisitor.php b/src/Engine/ExpressionVisitor.php deleted file mode 100644 index c1f3a52..0000000 --- a/src/Engine/ExpressionVisitor.php +++ /dev/null @@ -1,96 +0,0 @@ - - * @copyright 2024-present Smile - * @license Open Software License v. 3.0 (OSL-3.0) - */ - -declare(strict_types=1); - -namespace Gally\OroPlugin\Engine; - -use Doctrine\Common\Collections\Expr\Comparison; -use Doctrine\Common\Collections\Expr\CompositeExpression; -use Doctrine\Common\Collections\Expr\ExpressionVisitor as BaseExpressionVisitor; -use Doctrine\Common\Collections\Expr\Value; -use Gally\Sdk\GraphQl\Request; -use Oro\Bundle\SearchBundle\Query\Criteria\Criteria; - -class ExpressionVisitor extends BaseExpressionVisitor -{ - private ?string $searchQuery = null; - private ?string $currentCategoryId = null; - - public function walkCompositeExpression(CompositeExpression $expr): array - { - $filters = []; - foreach ($expr->getExpressionList() as $expression) { - $filters[] = $this->dispatch($expression); - } - - $type = CompositeExpression::TYPE_AND === $expr->getType() ? '_must' : '_should'; - - return ['boolFilter' => [$type => array_values(array_filter($filters))]]; - } - - public function walkComparison(Comparison $comparison): ?array - { - [$type, $field] = Criteria::explodeFieldTypeName($comparison->getField()); - $value = $this->dispatch($comparison->getValue()); - $operator = match ($comparison->getOperator()) { - 'IN' => Request::FILTER_OPERATOR_IN, - 'LIKE' => Request::FILTER_OPERATOR_MATCH, - default => Request::FILTER_OPERATOR_EQ, - // todo add EXISTS - }; - - if ('all_text' === $field) { - $this->searchQuery = $value; - - return null; - } - - if ('inv_status' === $field) { - return null; - } - - if ('category_path' === $field) { -// $this->currentCategoryId = 'node_' . basename(str_replace('_', '/', $value)); // todo this is wrong, the current category should contain content node id ! - - return null; - } - - if (str_starts_with($field, 'assigned_to')) { - return null; // Todo manage this - } - if (str_starts_with($field, 'manually_added_to')) { - return null; // Todo manage this - } - - if (str_starts_with($field, 'category_path')) { - return null; - } - - return [$field => [$operator => $value]]; - } - - public function walkValue(Value $value): mixed - { - return $value->getValue(); - } - - public function getCurrentCategoryId(): ?string - { - return $this->currentCategoryId; - } - - public function getSearchQuery(): ?string - { - return $this->searchQuery; - } -} diff --git a/src/Engine/SearchEngine.php b/src/Engine/SearchEngine.php index 0701193..677280b 100644 --- a/src/Engine/SearchEngine.php +++ b/src/Engine/SearchEngine.php @@ -15,7 +15,7 @@ namespace Gally\OroPlugin\Engine; use Gally\OroPlugin\Registry\SearchRegistry; -use Gally\OroPlugin\RequestBuilder\GallyRequestBuilder; +use Gally\OroPlugin\Search\GallyRequestBuilder; use Gally\Sdk\Service\SearchManager; use Oro\Bundle\SearchBundle\Provider\AbstractSearchMappingProvider; use Oro\Bundle\SearchBundle\Query\Query; diff --git a/src/Indexer/IndexDataProvider.php b/src/Indexer/IndexDataProvider.php index e163ebb..5c57455 100644 --- a/src/Indexer/IndexDataProvider.php +++ b/src/Indexer/IndexDataProvider.php @@ -135,9 +135,7 @@ private function prepareIndexData( } } - if (str_starts_with($fieldName, 'category_path')) { - // Todo - } elseif (str_starts_with($fieldName, 'ordered_at_by')) { + if (str_starts_with($fieldName, 'ordered_at_by')) { // Todo } elseif (!str_starts_with($fieldName, self::ALL_TEXT_PREFIX)) { if (null === $value || '' === $value || [] === $value) { diff --git a/src/Indexer/Normalizer/BooleanDataNormalizer.php b/src/Indexer/Normalizer/BooleanDataNormalizer.php index 67a5e74..ccb1ba2 100644 --- a/src/Indexer/Normalizer/BooleanDataNormalizer.php +++ b/src/Indexer/Normalizer/BooleanDataNormalizer.php @@ -44,7 +44,7 @@ public function preProcess( $fieldConfig = null; } $type = $fieldConfig ? $fieldConfig->getId()->getFieldType() : $fieldData['type']; - if ('boolean' === $type || str_starts_with($fieldName, 'is_')) { + if ('boolean' === $type) { $this->booleanAttributes[] = $fieldName; } } diff --git a/src/Indexer/Normalizer/CategoryDataNormalizer.php b/src/Indexer/Normalizer/CategoryDataNormalizer.php index ac8511c..0bab1fd 100644 --- a/src/Indexer/Normalizer/CategoryDataNormalizer.php +++ b/src/Indexer/Normalizer/CategoryDataNormalizer.php @@ -14,6 +14,7 @@ namespace Gally\OroPlugin\Indexer\Normalizer; +use Oro\Bundle\CatalogBundle\Placeholder\CategoryPathPlaceholder; use Oro\Bundle\EntityBundle\ORM\DoctrineHelper; use Oro\Bundle\LocaleBundle\Entity\Localization; use Oro\Bundle\LocaleBundle\Helper\LocalizationHelper; @@ -98,15 +99,11 @@ public function normalize( } } - $assignSortOrders = $fieldsValues['assigned_to_sort_order.ASSIGN_TYPE_ASSIGN_ID'] ?? []; - foreach ($assignSortOrders as $assignSortOrder) { - $value = $assignSortOrder['value']; + foreach ($fieldsValues['category_paths.CATEGORY_PATH'] ?? [] as $value) { + $value = $value['value']; if ($value instanceof PlaceholderValue) { $placeholders = $value->getPlaceholders(); - if ('variant' === $placeholders[AssignTypePlaceholder::NAME]) { - $variantId = $placeholders[AssignIdPlaceholder::NAME]; - $categories[$variantId]['position'] = $value->getValue(); - } + $preparedEntityData['category_paths'][] = $placeholders[CategoryPathPlaceholder::NAME]; } } @@ -124,5 +121,6 @@ public function normalize( unset($fieldsValues['assigned_to.ASSIGN_TYPE_ASSIGN_ID']); unset($fieldsValues['manually_added_to.ASSIGN_TYPE_ASSIGN_ID']); unset($fieldsValues['assigned_to_sort_order.ASSIGN_TYPE_ASSIGN_ID']); + unset($fieldsValues['category_paths.CATEGORY_PATH']); } } diff --git a/src/Indexer/Normalizer/VisibilityDataNormalizer.php b/src/Indexer/Normalizer/VisibilityDataNormalizer.php index 393e512..9c5eba0 100644 --- a/src/Indexer/Normalizer/VisibilityDataNormalizer.php +++ b/src/Indexer/Normalizer/VisibilityDataNormalizer.php @@ -16,6 +16,7 @@ use Oro\Bundle\CustomerBundle\Placeholder\CustomerIdPlaceholder; use Oro\Bundle\ProductBundle\Entity\Product; +use Oro\Bundle\VisibilityBundle\Entity\VisibilityResolved\BaseVisibilityResolved; use Oro\Bundle\WebsiteBundle\Entity\Website; use Oro\Bundle\WebsiteSearchBundle\Placeholder\PlaceholderValue; @@ -33,7 +34,8 @@ public function normalize( array &$preparedEntityData, ): void { if (Product::class === $entityClass) { - $visibilitiesByCustomer = []; + $visibleForCustomers = []; + $hiddenForCustomers = []; $visibilities = $fieldsValues['visibility_customer.CUSTOMER_ID'] ?? []; foreach ($this->toArray($visibilities) as $value) { $value = $value['value']; @@ -44,14 +46,18 @@ public function normalize( $value = $value->getValue(); } - $visibilitiesByCustomer[] = [ - 'customer_id' => $placeholders[CustomerIdPlaceholder::NAME], - 'value' => $value, - ]; + if (BaseVisibilityResolved::VISIBILITY_VISIBLE === $value) { + $visibleForCustomers[] = $placeholders[CustomerIdPlaceholder::NAME]; + } elseif (BaseVisibilityResolved::VISIBILITY_HIDDEN === $value) { + $hiddenForCustomers[] = $placeholders[CustomerIdPlaceholder::NAME]; + } } - if (!empty($visibilitiesByCustomer)) { - $preparedEntityData['visibility_customer'] = $visibilitiesByCustomer; + if (!empty($visibleForCustomers)) { + $preparedEntityData['visible_for_customer'] = $visibleForCustomers; + } + if (!empty($hiddenForCustomers)) { + $preparedEntityData['hidden_for_customer'] = $hiddenForCustomers; } unset($fieldsValues['visibility_customer.CUSTOMER_ID']); } diff --git a/src/Indexer/Provider/CatalogProvider.php b/src/Indexer/Provider/CatalogProvider.php index a1415a3..09e4b34 100644 --- a/src/Indexer/Provider/CatalogProvider.php +++ b/src/Indexer/Provider/CatalogProvider.php @@ -17,6 +17,7 @@ use Doctrine\ORM\EntityManagerInterface; use Gally\Sdk\Entity\Catalog; use Gally\Sdk\Entity\LocalizedCatalog; +use Oro\Bundle\LocaleBundle\Entity\Localization; use Oro\Bundle\PricingBundle\Provider\WebsiteCurrencyProvider; use Oro\Bundle\WebsiteBundle\Entity\Repository\WebsiteRepository; use Oro\Bundle\WebsiteBundle\Entity\Website; @@ -30,6 +31,8 @@ class CatalogProvider implements ProviderInterface protected WebsiteRepository $websiteRepository; protected AbstractWebsiteLocalizationProvider $websiteLocalizationProvider; + private array $catalogCache = []; + public function __construct( EntityManagerInterface $entityManager, AbstractWebsiteLocalizationProvider $websiteLocalizationProvider, @@ -47,16 +50,8 @@ public function provide(): iterable $websites = $this->websiteRepository->findAll(); foreach ($websites as $website) { - $localizations = $this->websiteLocalizationProvider->getLocalizations($website); - $catalog = new Catalog($this->getCatalogCodeFromWebsiteId($website->getId()), $website->getName()); - foreach ($localizations as $localization) { - yield new LocalizedCatalog( - $catalog, - 'website_' . $website->getId() . '_' . $localization->getFormattingCode(), - $localization->getName(), - $localization->getFormattingCode(), - $this->currencyProvider->getWebsiteDefaultCurrency($website->getId()) - ); + foreach ($this->websiteLocalizationProvider->getLocalizations($website) as $localization) { + yield $this->buildLocalizedCatalog($website, $localization); } } } @@ -65,4 +60,22 @@ public function getCatalogCodeFromWebsiteId(int $websiteId): string { return 'website_' . $websiteId; } + + public function buildLocalizedCatalog(Website $website, Localization $localization): LocalizedCatalog + { + if (!\array_key_exists($website->getId(), $this->catalogCache)) { + $this->catalogCache[$website->getId()] = new Catalog( + $this->getCatalogCodeFromWebsiteId($website->getId()), + $website->getName(), + ); + } + + return new LocalizedCatalog( + $this->catalogCache[$website->getId()], + 'website_' . $website->getId() . '_' . $localization->getFormattingCode(), + $localization->getName(), + $localization->getFormattingCode(), + $this->currencyProvider->getWebsiteDefaultCurrency($website->getId()) + ); + } } diff --git a/src/Indexer/Provider/SourceFieldProvider.php b/src/Indexer/Provider/SourceFieldProvider.php index 8cf7cca..b5264e1 100644 --- a/src/Indexer/Provider/SourceFieldProvider.php +++ b/src/Indexer/Provider/SourceFieldProvider.php @@ -44,6 +44,20 @@ class SourceFieldProvider implements ProviderInterface 'brand_LOCALIZATION_ID', // Brand field is managed as a select ]; + private array $oroSystemAttribute = [ // todo conf + 'status', + 'assigned_to', + 'manually_added_to', + 'category_path', + 'category_paths', + 'is_variant', + 'is_visible_by_default', + 'visibility_anonymous', + 'visibility_new', + 'visible_for_customer', + 'hidden_for_customer', + ]; + public function __construct( private SearchMappingProvider $mappingProvider, private EntityAliasResolver $entityAliasResolver, @@ -108,6 +122,7 @@ public function provide(): iterable $fieldType, $defaultLabel, $this->getLabels($labelKey, $defaultLabel), + \in_array($fieldName, $this->oroSystemAttribute, true) ); } } @@ -141,7 +156,6 @@ private function getGallyType(string $fieldName, string $fieldType): string 'brand' === $fieldName => SourceField::TYPE_SELECT, str_ends_with($fieldName, '_enum') => SourceField::TYPE_SELECT, str_starts_with($fieldName, 'image_') => SourceField::TYPE_IMAGE, - str_starts_with($fieldName, 'is_') => SourceField::TYPE_BOOLEAN, default => $this->typeMapping[$fieldType] ?? SourceField::TYPE_TEXT, }; } diff --git a/src/RequestBuilder/GallyRequestBuilder.php b/src/RequestBuilder/GallyRequestBuilder.php deleted file mode 100644 index 12927c3..0000000 --- a/src/RequestBuilder/GallyRequestBuilder.php +++ /dev/null @@ -1,145 +0,0 @@ - - * @copyright 2024-present Smile - * @license Open Software License v. 3.0 (OSL-3.0) - */ - -declare(strict_types=1); - -namespace Gally\OroPlugin\RequestBuilder; - -use Gally\OroPlugin\Engine\ExpressionVisitor; -use Gally\Sdk\Entity\Catalog; -use Gally\Sdk\Entity\LocalizedCatalog; -use Gally\Sdk\GraphQl\Request; -use Oro\Bundle\SearchBundle\Query\Criteria\Criteria; -use Oro\Bundle\SearchBundle\Query\Query; - -class GallyRequestBuilder -{ - public function build(Query $query, array $context): Request - { - if (!$this->isProductQuery($query)) { - // Todo ! - throw new \Exception('Todo manage this'); - } - - [$currentPage, $pageSize] = $this->getPaginationInfo($query); - [$sortField, $sortDirection] = $this->getSortInfo($query); - [$currentCategoryId, $searchQuery, $filters] = $this->getFilters($query); - - return new Request( - $this->getCurrentLocalizedCatalog(), - $this->getSelectedFields($query), - $currentPage, - $pageSize, - $currentCategoryId, - $searchQuery, - $filters, - $sortField, - $sortDirection, - ); - } - - private function isProductQuery(Query $query): bool - { - $from = $query->getFrom(); - - return 1 === \count($from) && str_starts_with($from[0], 'oro_product'); - } - - private function getCurrentLocalizedCatalog(): LocalizedCatalog - { - // Todo find context - $catalog = new Catalog('website_1', 'Test'); - - return new LocalizedCatalog( - $catalog, - // 'website_1_en_US', - 'website_1_fr_CA', - 'Blop', - 'en_US', - 'EUR' - ); - } - - private function getSelectedFields(Query $query): array - { - // Todo Clean field name - $fields = $query->getSelect(); - $selectedFields = empty($fields) ? [] : ['id']; - foreach ($fields as $field) { - [$type, $name] = Criteria::explodeFieldTypeName($field); - if ('names' === $name) { - $name = 'name'; - } - if ('system_entity_id' === $name) { - $name = 'id'; - } - if ('inv_status' == $name) { // todo - continue; - } - $selectedFields[] = $name; - } - - return $selectedFields; - } - - /** - * @return array{0: int, 1: int} - */ - private function getPaginationInfo(Query $query): array - { - $from = (int) $query->getCriteria()->getFirstResult(); - $pageSize = (int) $query->getCriteria()->getMaxResults() ?: 25; - $currentPage = (int) ceil($from / $pageSize) + 1; - - return [$currentPage, $pageSize]; - } - - /** - * @return array{0: ?string, 1: ?string} - */ - private function getSortInfo(Query $query): array - { - $orders = $query->getCriteria()->getOrderings(); - - if (!empty($orders)) { - // We can only use one sort order in gally (score and id ordering are added automatically). - $order = array_key_first($orders); - [$type, $field] = Criteria::explodeFieldTypeName($order); - - if ('category_sort_order' == $field || str_starts_with($field, 'assigned_to_sort_order.')) { - // todo manage this globally - $field = 'category__position'; - $field = '_score'; - } - - return [$field, 'ASC' === $orders[$order] ? Request::SORT_DIRECTION_ASC : Request::SORT_DIRECTION_DESC]; - } - - return [null, null]; - } - - /** - * @return array{0: ?string, 1: ?string, 2: array} - */ - private function getFilters(Query $query): array - { - $visitor = new ExpressionVisitor(); - $filters = []; - - if ($expression = $query->getCriteria()->getWhereExpression()) { - $filters = $visitor->dispatch($expression); - } - - // return [$visitor->getCurrentCategoryId(), $visitor->getSearchQuery(), [$filters]]; - return [$visitor->getCurrentCategoryId(), $visitor->getSearchQuery(), []]; - } -} diff --git a/src/Resources/config/oro/website_search.yml b/src/Resources/config/oro/website_search.yml index f053c3c..4481a30 100644 --- a/src/Resources/config/oro/website_search.yml +++ b/src/Resources/config/oro/website_search.yml @@ -21,14 +21,16 @@ Oro\Bundle\ProductBundle\Entity\Brand: Oro\Bundle\ProductBundle\Entity\Product: fields: - - name: id - type: integer - name: inv_qty type: decimal - - name: visibility_customer.customer_id + - name: category_paths + type: text + - name: category_paths.CATEGORY_PATH + type: text + - name: visible_for_customer + type: text + - name: hidden_for_customer type: text - - name: visibility_customer.value - type: integer Oro\Bundle\WebsiteSearchSuggestionBundle\Entity\Suggestion: fields: diff --git a/src/Resources/config/services/search.yml b/src/Resources/config/services/search.yml index 306080f..509feb9 100644 --- a/src/Resources/config/services/search.yml +++ b/src/Resources/config/services/search.yml @@ -1,9 +1,23 @@ services: Gally\OroPlugin\Registry\SearchRegistry: ~ - Gally\OroPlugin\RequestBuilder\GallyRequestBuilder: ~ + Gally\OroPlugin\Search\ContextProvider: + arguments: + - '@oro_website.manager' + - '@oro_locale.helper.localization' + - '@Gally\OroPlugin\Indexer\Provider\CatalogProvider' + - '@oro_web_catalog.request_web_content_variant_provider' + + Gally\OroPlugin\Search\ExpressionVisitor: + arguments: + - '%gally_config.attribute_mapping%' + + Gally\OroPlugin\Search\GallyRequestBuilder: + arguments: + - '@Gally\OroPlugin\Search\ContextProvider' + - '@Gally\OroPlugin\Search\ExpressionVisitor' - Gally\OroPlugin\Filter\SelectFilter: + Gally\OroPlugin\Search\Filter\SelectFilter: public: false arguments: - '@form.factory' @@ -12,7 +26,7 @@ services: tags: - { name: oro_search.extension.search_filter.filter, type: gally-select } - Gally\OroPlugin\Form\SelectFormFilter: + Gally\OroPlugin\Search\Filter\Form\SelectFormFilter: tags: - { name: form.type, alias: gally_search_type_select_filter } @@ -24,7 +38,7 @@ services: # - '@oro_website_elastic_search.engine.index_agent' # - '@oro_website_elastic_search.request_builder.registry' - '@Gally\Sdk\Service\SearchManager' - - '@Gally\OroPlugin\RequestBuilder\GallyRequestBuilder' + - '@Gally\OroPlugin\Search\GallyRequestBuilder' - '@Gally\OroPlugin\Registry\SearchRegistry' - '%gally_config.attribute_mapping%' calls: @@ -32,7 +46,7 @@ services: tags: - { name: 'oro_website_search.engine', engine_name: 'gally' } - Gally\OroPlugin\Extension\GallyDataGridExtension: + Gally\OroPlugin\Search\Extension\GallyDataGridExtension: arguments: - '@oro_website_search.engine.parameters' - '@Gally\Sdk\Service\SearchManager' diff --git a/src/Search/ContextProvider.php b/src/Search/ContextProvider.php index 9573268..b1223a3 100644 --- a/src/Search/ContextProvider.php +++ b/src/Search/ContextProvider.php @@ -16,7 +16,6 @@ use Gally\OroPlugin\Indexer\Provider\CatalogProvider; use Gally\Sdk\Entity\LocalizedCatalog; -use Oro\Bundle\CatalogBundle\Handler\RequestProductHandler; use Oro\Bundle\LocaleBundle\Entity\Localization; use Oro\Bundle\LocaleBundle\Helper\LocalizationHelper; use Oro\Bundle\WebCatalogBundle\Entity\ContentNode; diff --git a/src/Search/ExpressionVisitor.php b/src/Search/ExpressionVisitor.php index cceceb9..5f795f4 100644 --- a/src/Search/ExpressionVisitor.php +++ b/src/Search/ExpressionVisitor.php @@ -20,17 +20,22 @@ use Doctrine\Common\Collections\Expr\ExpressionVisitor as BaseExpressionVisitor; use Doctrine\Common\Collections\Expr\Value; use Gally\Sdk\GraphQl\Request; +use Oro\Bundle\ProductBundle\Entity\Product; use Oro\Bundle\SearchBundle\Query\Criteria\Criteria; +use Oro\Bundle\VisibilityBundle\Entity\VisibilityResolved\BaseVisibilityResolved; class ExpressionVisitor extends BaseExpressionVisitor { + public function __construct( + private array $attributeMapping, + ) { + } + private ?string $searchQuery = null; - private ?string $currentCategoryId = null; public function dispatch(Expression $expr, bool $isMainQuery = true) { // Use main query parameter to flatten main and expression. - switch (true) { case $expr instanceof Comparison: return $this->walkComparison($expr); @@ -65,13 +70,15 @@ public function walkCompositeExpression(CompositeExpression $expr, bool $isMainQ public function walkComparison(Comparison $comparison): ?array { [$type, $field] = Criteria::explodeFieldTypeName($comparison->getField()); + $field = $this->attributeMapping[$field] ?? $field; $value = $this->dispatch($comparison->getValue()); $operator = match ($comparison->getOperator()) { - 'IN' => Request::FILTER_OPERATOR_IN, - 'LIKE' => Request::FILTER_OPERATOR_MATCH, + 'IN', 'NOT IN' => Request::FILTER_OPERATOR_IN, + 'LIKE', 'NOT LIKE' => Request::FILTER_OPERATOR_MATCH, + 'EXISTS', 'NOT EXISTS' => Request::FILTER_OPERATOR_EXISTS, default => Request::FILTER_OPERATOR_EQ, - // todo add EXISTS }; + $hasNegation = str_starts_with($comparison->getOperator(), 'NOT'); if ('all_text' === $field) { $this->searchQuery = $value; @@ -79,36 +86,44 @@ public function walkComparison(Comparison $comparison): ?array return null; } - if ('inv_status' === $field) { + if ('id' === $field) { + $type = 'text'; + } elseif ('inv_status' === $field) { $field = 'stock.status'; - if (count($value) > 1) { + if (\count($value) > 1) { return null; // if we want in stock and out of sotck product, we do not need this filter. } $operator = Request::FILTER_OPERATOR_EQ; - $value = in_array(\Oro\Bundle\ProductBundle\Entity\Product::INVENTORY_STATUS_IN_STOCK, $value, true); - // return null; //todo mange specificque code for code stock - } elseif (str_starts_with($field, 'visibility_customer.')) { + $value = \in_array(Product::INVENTORY_STATUS_IN_STOCK, $value, true); + } elseif (str_starts_with($field, 'assigned_to.') || str_starts_with($field, 'manually_added_to.')) { + [$field, $variantId] = explode('.', $field); + [$_, $value] = explode('_', $variantId); + } elseif (str_starts_with($field, 'category_paths.')) { [$field, $value] = explode('.', $field); + $operator = Request::FILTER_OPERATOR_IN; + } elseif (str_starts_with($field, 'visibility_customer.')) { + [$field, $customerId] = explode('.', $field); + $type = 'text'; + if (Request::FILTER_OPERATOR_EXISTS === $operator) { + $field = 'boolFilter'; + $operator = '_must'; + $value = [ + ['visible_for_customer' => [Request::FILTER_OPERATOR_EQ => $customerId]], + ['hidden_for_customer' => [Request::FILTER_OPERATOR_EQ => $customerId]], + ]; + } elseif (Request::FILTER_OPERATOR_EQ == $operator) { + $field = BaseVisibilityResolved::VISIBILITY_HIDDEN === $value + ? 'hidden_for_customer' + : 'visible_for_customer'; + $value = $customerId; + } } - if ('category_path' === $field) { - // $this->currentCategoryId = 'node_' . basename(str_replace('_', '/', $value)); // todo this is wrong, the current category should contain content node id ! - - // return null; - } - - if (str_starts_with($field, 'assigned_to')) { - return null; // Todo manage this - } - if (str_starts_with($field, 'manually_added_to')) { - return null; // Todo manage this - } - - if (str_starts_with($field, 'category_path')) { - // return null; - } + $rule = [$field => [$operator => $this->enforceValueType($type, $value)]]; - return [$field => [$operator => $value]]; + return $hasNegation + ? ['boolFilter' => ['_not' => [$rule]]] + : $rule; } public function walkValue(Value $value): mixed @@ -116,13 +131,23 @@ public function walkValue(Value $value): mixed return $value->getValue(); } - public function getCurrentCategoryId(): ?string + public function getSearchQuery(): ?string { - return $this->currentCategoryId; + return $this->searchQuery; } - public function getSearchQuery(): ?string + private function enforceValueType(string $type, mixed $value): mixed { - return $this->searchQuery; + if (\is_array($value)) { + return array_map(fn ($item) => $this->enforceValueType($type, $item), $value); + } + + return match ($type) { + 'int' => (int) $value, + 'float' => (float) $value, + 'bool' => (bool) $value, + 'text' => (string) $value, + default => $value, + }; } } diff --git a/src/Extension/GallyDataGridExtension.php b/src/Search/Extension/GallyDataGridExtension.php similarity index 96% rename from src/Extension/GallyDataGridExtension.php rename to src/Search/Extension/GallyDataGridExtension.php index adc1d52..6d822ea 100644 --- a/src/Extension/GallyDataGridExtension.php +++ b/src/Search/Extension/GallyDataGridExtension.php @@ -12,7 +12,7 @@ declare(strict_types=1); -namespace Gally\OroPlugin\Extension; +namespace Gally\OroPlugin\Search\Extension; use Gally\OroPlugin\Engine\SearchEngine; use Gally\OroPlugin\Registry\SearchRegistry; @@ -55,8 +55,6 @@ public function visitDatasource(DatagridConfiguration $config, DatasourceInterfa public function visitMetadata(DatagridConfiguration $config, MetadataObject $object) { - // $config->offsetSetByPath('[filters][columns]', []); - $blop = 'toto'; $this->addFiltersFromGallyResult($config, $object); } @@ -76,7 +74,16 @@ private function addSortFieldsFromGallyConfiguration(DatagridConfiguration $conf $sorters[$attribute->getCode()] = $attribute; $config->offsetSetByPath( '[sorters][columns][' . $attribute->getCode() . ']', - ['data_name' => $attribute->getCode()] + array_filter( + [ + 'data_name' => $attribute->getCode(), + 'type' => match ($attribute->getType()) { + SourceField::TYPE_TEXT => 'string', + default => null + } + ] + ) + ); } @@ -117,7 +124,7 @@ private function addFilterFieldsFromGallyConfiguration(DatagridConfiguration $co if (!\in_array($sourceField->getCode(), $proceed, true)) { $filter = [ 'data_name' => $sourceField->getCode(), - 'label' => $sourceField->getDefaultLabel() . ' Gally filter', // todo, + 'label' => $sourceField->getDefaultLabel(), 'visible' => false, 'disabled' => false, 'renderable' => true, @@ -170,7 +177,7 @@ private function addFiltersFromGallyResult(DatagridConfiguration $config, Metada foreach ($gallyFilters as $gallyFilter) { $filter = [ 'data_name' => $gallyFilter['field'], - 'label' => $gallyFilter['label'] . ' Gally filter', // todo + 'label' => $gallyFilter['label'], 'visible' => true, 'disabled' => false, 'renderable' => true, diff --git a/src/Form/ChoiceLoader.php b/src/Search/Filter/Form/ChoiceLoader.php similarity index 96% rename from src/Form/ChoiceLoader.php rename to src/Search/Filter/Form/ChoiceLoader.php index a844b60..4a2f784 100644 --- a/src/Form/ChoiceLoader.php +++ b/src/Search/Filter/Form/ChoiceLoader.php @@ -12,7 +12,7 @@ declare(strict_types=1); -namespace Gally\OroPlugin\Form; +namespace Gally\OroPlugin\Search\Filter\Form; use Symfony\Component\Form\ChoiceList\ArrayChoiceList; use Symfony\Component\Form\ChoiceList\ChoiceListInterface; diff --git a/src/Form/SelectFormFilter.php b/src/Search/Filter/Form/SelectFormFilter.php similarity index 98% rename from src/Form/SelectFormFilter.php rename to src/Search/Filter/Form/SelectFormFilter.php index 6a15ba6..26923c5 100644 --- a/src/Form/SelectFormFilter.php +++ b/src/Search/Filter/Form/SelectFormFilter.php @@ -12,7 +12,7 @@ declare(strict_types=1); -namespace Gally\OroPlugin\Form; +namespace Gally\OroPlugin\Search\Filter\Form; use Oro\Bundle\FilterBundle\Form\Type\Filter\ChoiceFilterType; use Symfony\Component\Form\AbstractType; diff --git a/src/Filter/SelectFilter.php b/src/Search/Filter/SelectFilter.php similarity index 95% rename from src/Filter/SelectFilter.php rename to src/Search/Filter/SelectFilter.php index fb7f8a7..70b74c0 100644 --- a/src/Filter/SelectFilter.php +++ b/src/Search/Filter/SelectFilter.php @@ -12,9 +12,9 @@ declare(strict_types=1); -namespace Gally\OroPlugin\Filter; +namespace Gally\OroPlugin\Search\Filter; -use Gally\OroPlugin\Form\SelectFormFilter; +use Gally\OroPlugin\Search\Filter\Form\SelectFormFilter; use Oro\Bundle\FilterBundle\Datasource\FilterDatasourceAdapterInterface; use Oro\Bundle\FilterBundle\Filter\BaseMultiChoiceFilter; use Oro\Bundle\FilterBundle\Filter\FilterUtility; diff --git a/src/Search/GallyRequestBuilder.php b/src/Search/GallyRequestBuilder.php index 99902cf..4875e42 100644 --- a/src/Search/GallyRequestBuilder.php +++ b/src/Search/GallyRequestBuilder.php @@ -21,7 +21,8 @@ class GallyRequestBuilder { public function __construct( - private ContextProvider $contextProvider + private ContextProvider $contextProvider, + private ExpressionVisitor $expressionVisitor, ) { } @@ -120,13 +121,12 @@ private function getSortInfo(Query $query): array */ private function getFilters(Query $query): array { - $visitor = new ExpressionVisitor(); $filters = []; if ($expression = $query->getCriteria()->getWhereExpression()) { - $filters = $visitor->dispatch($expression); + $filters = $this->expressionVisitor->dispatch($expression); } - return [$visitor->getSearchQuery(), [$filters]]; + return [$this->expressionVisitor->getSearchQuery(), [$filters]]; } }