diff --git a/src/Decorator/SavePriceFilterUnit.php b/src/Decorator/SavePriceFilterUnit.php new file mode 100644 index 0000000..1d09512 --- /dev/null +++ b/src/Decorator/SavePriceFilterUnit.php @@ -0,0 +1,82 @@ + + * @copyright 2024-present Smile + * @license Open Software License v. 3.0 (OSL-3.0) + */ + +declare(strict_types=1); + +namespace Gally\OroPlugin\Decorator; + +use Gally\OroPlugin\Search\SearchRegistry; +use Oro\Bundle\FilterBundle\Datasource\FilterDatasourceAdapterInterface; +use Oro\Bundle\FilterBundle\Filter\FilterInterface; +use Oro\Bundle\SearchBundle\Datagrid\Filter\SearchNumberFilter; + +class SavePriceFilterUnit implements FilterInterface +{ + public function __construct( + private SearchNumberFilter $decorated, + private SearchRegistry $searchRegistry, + ) { + } + + public function init($name, array $params) + { + $this->decorated->init($name, $params); + } + + public function getName() + { + return $this->decorated->getName(); + } + + public function getForm() + { + return $this->decorated->getForm(); + } + + public function getMetadata() + { + return $this->decorated->getMetadata(); + } + + public function resolveOptions() + { + return $this->decorated->resolveOptions(); + } + + public function apply(FilterDatasourceAdapterInterface $ds, $data): void + { + if (isset($data['unit'])) { + $this->searchRegistry->setPriceFilterUnit($data['unit']); + } + $this->decorated->apply($ds, $data); + } + + public function prepareData(array $data): array + { + return $this->decorated->prepareData($data); + } + + public function setFilterState($state): void + { + $this->decorated->setFilterState($state); + } + + public function getFilterState() + { + return $this->decorated->getFilterState(); + } + + public function reset(): void + { + $this->decorated->reset(); + } +} diff --git a/src/Indexer/Normalizer/PriceDataNormalizer.php b/src/Indexer/Normalizer/PriceDataNormalizer.php index 99777f7..45288d8 100644 --- a/src/Indexer/Normalizer/PriceDataNormalizer.php +++ b/src/Indexer/Normalizer/PriceDataNormalizer.php @@ -14,6 +14,7 @@ namespace Gally\OroPlugin\Indexer\Normalizer; +use Gally\OroPlugin\Resolver\PriceGroupResolver; use Oro\Bundle\ConfigBundle\Config\ConfigManager; use Oro\Bundle\EntityBundle\ORM\DoctrineHelper; use Oro\Bundle\FeatureToggleBundle\Checker\FeatureChecker; @@ -24,6 +25,7 @@ use Oro\Bundle\PricingBundle\Placeholder\CPLIdPlaceholder; use Oro\Bundle\PricingBundle\Placeholder\CurrencyPlaceholder; use Oro\Bundle\PricingBundle\Placeholder\PriceListIdPlaceholder; +use Oro\Bundle\PricingBundle\Placeholder\UnitPlaceholder; use Oro\Bundle\PricingBundle\Provider\WebsiteCurrencyProvider; use Oro\Bundle\ProductBundle\Entity\Product; use Oro\Bundle\WebsiteBundle\Entity\Website; @@ -39,6 +41,7 @@ public function __construct( private ConfigManager $configManager, private FeatureChecker $featureChecker, private WebsiteCurrencyProvider $currencyProvider, + private PriceGroupResolver $priceGroupResolver, ) { } @@ -64,7 +67,10 @@ public function normalize( ): void { if (Product::class === $entityClass) { $prices = []; - $minimalPrices = $fieldsValues['minimal_price.CPL_ID_CURRENCY'] ?? []; + $minimalPrices = array_merge( + $fieldsValues['minimal_price.CPL_ID_CURRENCY'] ?? [], + $fieldsValues['minimal_price.CPL_ID_CURRENCY_UNIT'] ?? [], + ); foreach ($this->toArray($minimalPrices) as $value) { $value = $value['value']; $placeholders = []; @@ -74,15 +80,23 @@ public function normalize( $value = $value->getValue(); } - if ($this->defaultCurrency !== $placeholders[CurrencyPlaceholder::NAME]) { - continue; + $priceListId = $placeholders[CPLIdPlaceholder::NAME] ?: $placeholders[PriceListIdPlaceholder::NAME]; + $prices[] = [ + 'price' => (float) $value, + 'group_id' => $this->priceGroupResolver->getGroupId( + isset($placeholders[CPLIdPlaceholder::NAME]), + $priceListId, + $placeholders[CurrencyPlaceholder::NAME], + $placeholders[UnitPlaceholder::NAME] ?? null + ), + ]; + + if ($this->defaultPriceList->getId() === $priceListId + && $this->defaultCurrency === $placeholders[CurrencyPlaceholder::NAME] + && !isset($placeholders[UnitPlaceholder::NAME]) + ) { + array_unshift($prices, ['price' => (float) $value, 'group_id' => 0]); } - - $groupId = $placeholders[CPLIdPlaceholder::NAME] ?: $placeholders[PriceListIdPlaceholder::NAME]; - $groupId = $this->defaultPriceList->getId() === $groupId - ? 0 - : (($placeholders[CPLIdPlaceholder::NAME] ? 'cpl_' : 'pl_') . $groupId); - $prices[] = ['price' => (float) $value, 'group_id' => $groupId]; } if (!empty($prices)) { diff --git a/src/Resolver/PriceGroupResolver.php b/src/Resolver/PriceGroupResolver.php index f772c0a..f9431f8 100644 --- a/src/Resolver/PriceGroupResolver.php +++ b/src/Resolver/PriceGroupResolver.php @@ -1,8 +1,31 @@ + * @copyright 2024-present Smile + * @license Open Software License v. 3.0 (OSL-3.0) + */ + +declare(strict_types=1); namespace Gally\OroPlugin\Resolver; class PriceGroupResolver { - + public function getGroupId(bool $isCPLUsed, int $priceListId, string $currency, ?string $unit): string + { + return implode( + '_', + array_filter([ + $isCPLUsed ? 'cpl' : 'pl', + $priceListId, + $currency, + $unit, + ]) + ); + } } diff --git a/src/Resolver/QueryPlaceholderResolver.php b/src/Resolver/QueryPlaceholderResolver.php deleted file mode 100644 index 1b51050..0000000 --- a/src/Resolver/QueryPlaceholderResolver.php +++ /dev/null @@ -1,116 +0,0 @@ - - * @copyright 2024-present Smile - * @license Open Software License v. 3.0 (OSL-3.0) - */ - -declare(strict_types=1); - -namespace Gally\OroPlugin\Resolver; - -use Oro\Bundle\SearchBundle\Query\Query; -use Oro\Bundle\WebsiteSearchBundle\Placeholder\PlaceholderExpressionVisitor; -use Oro\Bundle\WebsiteSearchBundle\Placeholder\PlaceholderInterface; -use Oro\Bundle\WebsiteSearchBundle\Resolver\QueryPlaceholderResolverInterface; - -/** - * Provides functionality to replace placeholders with their values in field names in all parts of a search query. - * Todo useless ? - */ -class QueryPlaceholderResolver implements QueryPlaceholderResolverInterface -{ - public function __construct( - private PlaceholderInterface $placeholder, - ) { - } - - /** - * {@inheritdoc} - */ - public function replace(Query $query) - { - $this->replaceInSelect($query); - $this->replaceInFrom($query); - $this->replaceInCriteria($query); - $this->replaceInAggregations($query); - } - - private function replaceInSelect(Query $query) - { - $selectAliases = $query->getSelectAliases(); - $newSelects = []; - foreach ($query->getSelect() as $select) { - $newSelect = $this->placeholder->replaceDefault($select); - if (isset($selectAliases[$select])) { - $newSelect .= ' as ' . $selectAliases[$select]; - } - - $newSelects[] = $newSelect; - } - - $query->select($newSelects); - } - - /** - * @return Query - */ - private function replaceInFrom(Query $query) - { - $newEntities = []; - $from = $query->getFrom(); - - // This check required because getFrom can return false - if ($from) { - foreach ($from as $alias) { - $newEntities[] = $this->placeholder->replaceDefault($alias); - } - } - - return $query->from($newEntities); - } - - private function replaceInCriteria(Query $query) - { - $criteria = $query->getCriteria(); - $whereExpr = $criteria->getWhereExpression(); - - if ($whereExpr) { - $visitor = new PlaceholderExpressionVisitor($this->placeholder); - $criteria->where($visitor->dispatch($whereExpr)); - } - - $orderings = $criteria->getOrderings(); - if ($orderings) { - foreach ($orderings as $field => $ordering) { - unset($orderings[$field]); - $alteredField = $this->placeholder->replaceDefault($field); - $orderings[$alteredField] = $ordering; - } - $criteria->orderBy($orderings); - } - } - - private function replaceInAggregations(Query $query) - { - $aggregations = $query->getAggregations(); - if (!$aggregations) { - return; - } - - $newAggregations = []; - foreach ($aggregations as $name => $item) { - $newAggregations[$name] = [ - 'field' => $this->placeholder->replaceDefault($item['field']), - 'function' => $item['function'], - 'parameters' => $item['parameters'], - ]; - } - $query->setAggregations($newAggregations); - } -} diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 8480116..e8f8550 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -22,10 +22,14 @@ services: - '@oro_website_search.placeholder.registry' Gally\OroPlugin\Resolver\QueryPlaceholderResolver: + class: Oro\Bundle\WebsiteSearchBundle\Resolver\QueryPlaceholderResolver public: false arguments: - '@Gally\OroPlugin\Placeholder\PlaceholderDecorator' + Gally\OroPlugin\Resolver\PriceGroupResolver: + public: false + oro_website_elastic_search.voter.elastic_search_engine_feature_voter: class: Gally\OroPlugin\Voter\ElasticSearchEngineFeatureVoter arguments: diff --git a/src/Resources/config/services/indexer.yml b/src/Resources/config/services/indexer.yml index 65a67f8..a3a1e5c 100644 --- a/src/Resources/config/services/indexer.yml +++ b/src/Resources/config/services/indexer.yml @@ -81,6 +81,7 @@ services: - '@oro_config.manager' - '@oro_featuretoggle.checker.feature_checker' - '@oro_pricing.provider.website_currency_provider' + - '@Gally\OroPlugin\Resolver\PriceGroupResolver' tags: - { name: gally.indexer_normalizer } diff --git a/src/Resources/config/services/search.yml b/src/Resources/config/services/search.yml index 13ae121..7e5fc8d 100644 --- a/src/Resources/config/services/search.yml +++ b/src/Resources/config/services/search.yml @@ -1,6 +1,12 @@ services: Gally\OroPlugin\Search\SearchRegistry: ~ + Gally\OroPlugin\Decorator\SavePriceFilterUnit: + decorates: oro_pricing.filter.frontend_product_price + arguments: + - '@.inner' + - '@Gally\OroPlugin\Search\SearchRegistry' + Gally\OroPlugin\Search\ContextProvider: arguments: - '@oro_website.manager' @@ -16,6 +22,9 @@ services: arguments: - '@Gally\OroPlugin\Search\ContextProvider' - '@Gally\OroPlugin\Search\ExpressionVisitor' + - '@Gally\OroPlugin\Search\SearchRegistry' + - '@Gally\OroPlugin\Resolver\PriceGroupResolver' + - '@oro_website_search.placeholder.registry' Gally\OroPlugin\Search\Filter\SelectFilter: public: false @@ -46,6 +55,14 @@ services: tags: - { name: 'oro_website_search.engine', engine_name: 'gally' } + Gally\OroPlugin\Search\CustomerPartialUpdateDriver: + arguments: + - '@oro_website_search.provider.placeholder_provider' + - '@oro_visibility.visibility.provider.product_visibility_provider' + - '@oro_entity.doctrine_helper' + tags: + - { name: 'oro_website_search.customer_partial_update_driver', engine_name: 'gally' } + Gally\OroPlugin\Search\Extension\GallyDataGridExtension: arguments: - '@oro_website_search.engine.parameters' diff --git a/src/Search/CustomerPartialUpdateDriver.php b/src/Search/CustomerPartialUpdateDriver.php index c647dc4..2093686 100644 --- a/src/Search/CustomerPartialUpdateDriver.php +++ b/src/Search/CustomerPartialUpdateDriver.php @@ -1,20 +1,27 @@ + * @copyright 2024-present Smile + * @license Open Software License v. 3.0 (OSL-3.0) + */ + +declare(strict_types=1); namespace Gally\OroPlugin\Search; use Oro\Bundle\CustomerBundle\Entity\Customer; use Oro\Bundle\EntityBundle\ORM\DoctrineHelper; -use Oro\Bundle\SearchBundle\Query\Criteria\Criteria; -use Oro\Bundle\SearchBundle\Query\Query; use Oro\Bundle\VisibilityBundle\Driver\AbstractCustomerPartialUpdateDriver; -use Oro\Bundle\VisibilityBundle\Entity\VisibilityResolved\BaseVisibilityResolved; -use Oro\Bundle\VisibilityBundle\Indexer\ProductVisibilityIndexer; use Oro\Bundle\VisibilityBundle\Visibility\Provider\ProductVisibilityProvider; -use Oro\Bundle\WebsiteElasticSearchBundle\Manager\ElasticSearchPartialUpdateManager; use Oro\Bundle\WebsiteSearchBundle\Provider\PlaceholderProvider; /** - * Driver for the partial update of the customer visibility in the website search index + * Driver for the partial update of the customer visibility in the website search index. */ class CustomerPartialUpdateDriver extends AbstractCustomerPartialUpdateDriver { diff --git a/src/Search/ExpressionVisitor.php b/src/Search/ExpressionVisitor.php index b509800..0f6e953 100644 --- a/src/Search/ExpressionVisitor.php +++ b/src/Search/ExpressionVisitor.php @@ -107,7 +107,7 @@ public function walkComparison(Comparison $comparison): ?array } $type = 'bool'; $field = 'stock__status'; - $value = \in_array(Product::INVENTORY_STATUS_IN_STOCK, $value, true) || \in_array(true, $value, true); + $value = \in_array(Product::INVENTORY_STATUS_IN_STOCK, $value, true) || \in_array(1, $value, true); } elseif (str_starts_with($field, 'assigned_to.') || str_starts_with($field, 'manually_added_to.')) { [$field, $variantId] = explode('.', $field); [$_, $value] = explode('_', $variantId); diff --git a/src/Search/GallyRequestBuilder.php b/src/Search/GallyRequestBuilder.php index 066d43c..d6ee33d 100644 --- a/src/Search/GallyRequestBuilder.php +++ b/src/Search/GallyRequestBuilder.php @@ -14,15 +14,23 @@ namespace Gally\OroPlugin\Search; +use Gally\OroPlugin\Resolver\PriceGroupResolver; use Gally\Sdk\GraphQl\Request; +use Oro\Bundle\PricingBundle\Placeholder\CPLIdPlaceholder; +use Oro\Bundle\PricingBundle\Placeholder\CurrencyPlaceholder; +use Oro\Bundle\PricingBundle\Placeholder\PriceListIdPlaceholder; use Oro\Bundle\SearchBundle\Query\Criteria\Criteria; use Oro\Bundle\SearchBundle\Query\Query; +use Oro\Bundle\WebsiteSearchBundle\Placeholder\PlaceholderRegistry; class GallyRequestBuilder { public function __construct( private ContextProvider $contextProvider, private ExpressionVisitor $expressionVisitor, + private SearchRegistry $searchRegistry, + private PriceGroupResolver $priceGroupResolver, + private PlaceholderRegistry $registry, ) { } @@ -48,6 +56,7 @@ public function build(Query $query, array $context): Request $filters, $sortField, $sortDirection, + $this->getPriceGroup(), ); } @@ -128,4 +137,18 @@ private function getFilters(Query $query): array return [$this->expressionVisitor->getSearchQuery(), [$filters]]; } + + private function getPriceGroup(): string + { + $cplId = $this->registry->getPlaceholder(CPLIdPlaceholder::NAME)->getDefaultValue(); + $plId = $this->registry->getPlaceholder(PriceListIdPlaceholder::NAME)->getDefaultValue(); + $currency = $this->registry->getPlaceholder(CurrencyPlaceholder::NAME)->getDefaultValue(); + + return $this->priceGroupResolver->getGroupId( + (bool) $cplId, + (int) ($cplId ?: $plId), + $currency, + $this->searchRegistry->getPriceFilterUnit() + ); + } } diff --git a/src/Search/SearchRegistry.php b/src/Search/SearchRegistry.php index 74e3e1b..beedd1a 100644 --- a/src/Search/SearchRegistry.php +++ b/src/Search/SearchRegistry.php @@ -22,6 +22,7 @@ class SearchRegistry { private Response $response; + private ?string $priceFilterUnit = null; public function getResponse(): Response { @@ -32,4 +33,14 @@ public function setResponse(Response $response): void { $this->response = $response; } + + public function getPriceFilterUnit(): ?string + { + return $this->priceFilterUnit; + } + + public function setPriceFilterUnit(string $priceFilterUnit): void + { + $this->priceFilterUnit = $priceFilterUnit; + } }