diff --git a/config/services/controller.xml b/config/services/controller.xml index ea545f6b..f05371e4 100644 --- a/config/services/controller.xml +++ b/config/services/controller.xml @@ -23,6 +23,17 @@ %bitbag_es_shop_product_price_property_prefix% + + + + + + %bitbag_es_shop_product_sold_units% + %bitbag_es_shop_product_created_at% + %bitbag_es_shop_product_price_property_prefix% + %bitbag_es_shop_taxon_position_property_prefix% + + @@ -30,7 +41,7 @@ - + diff --git a/config/twig/bitbag_twig_hooks.yml b/config/twig/bitbag_twig_hooks.yml index 364337e7..c26d17c1 100644 --- a/config/twig/bitbag_twig_hooks.yml +++ b/config/twig/bitbag_twig_hooks.yml @@ -167,6 +167,13 @@ sylius_twig_hooks: template: '@SyliusShop/product/index/content/body/main/filters/controls/sorting/menu.html.twig' priority: 0 'bitbag.sylius_elasticsearch_plugin.taxon_products_search.index.content.body.main.filters.controls.sorting.menu': + position: + template: '@BitBagSyliusElasticsearchPlugin/Shop/TaxonProductsSearch/content/body/main/sorting/item.html.twig' + configuration: + title: 'bitbag_sylius_elasticsearch_plugin.ui.position' + order_by: 'taxon_position' + sort: 'asc' + priority: 700 bestsellers: template: '@BitBagSyliusElasticsearchPlugin/Shop/TaxonProductsSearch/content/body/main/sorting/item.html.twig' configuration: diff --git a/spec/Controller/RequestDataHandler/TaxonProductsSortDataHandlerSpec.php b/spec/Controller/RequestDataHandler/TaxonProductsSortDataHandlerSpec.php new file mode 100644 index 00000000..5e3d079a --- /dev/null +++ b/spec/Controller/RequestDataHandler/TaxonProductsSortDataHandlerSpec.php @@ -0,0 +1,127 @@ +beConstructedWith( + $channelPricingNameResolver, + $channelContext, + $taxonContext, + $taxonPositionNameResolver, + 'sold_units', + 'created_at', + 'price', + 'taxon_position' + ); + } + + function it_is_initializable(): void + { + $this->shouldHaveType(TaxonProductsSortDataHandler::class); + } + + function it_is_a_shop_products_sort_data_handler(): void + { + $this->shouldHaveType(ShopProductsSortDataHandler::class); + } + + function it_implements_sort_data_handler_interface(): void + { + $this->shouldHaveType(SortDataHandlerInterface::class); + } + + function it_sorts_by_taxon_position_ascending_by_default( + TaxonContextInterface $taxonContext, + TaxonInterface $taxon, + ConcatedNameResolverInterface $taxonPositionNameResolver, + ): void { + $taxonContext->getTaxon()->willReturn($taxon); + $taxon->getCode()->willReturn('t_shirts'); + $taxonPositionNameResolver->resolvePropertyName('t_shirts')->willReturn('taxon_position_t_shirts'); + + $this->retrieveData([])->shouldBeEqualTo([ + 'sort' => [ + 'taxon_position_t_shirts' => [ + 'order' => SortDataHandlerInterface::SORT_ASC_INDEX, + 'unmapped_type' => 'keyword', + ], + ], + ]); + } + + function it_resolves_the_taxon_position_field_for_the_current_taxon( + TaxonContextInterface $taxonContext, + TaxonInterface $taxon, + ConcatedNameResolverInterface $taxonPositionNameResolver, + ): void { + $taxonContext->getTaxon()->willReturn($taxon); + $taxon->getCode()->willReturn('mugs'); + $taxonPositionNameResolver->resolvePropertyName('mugs')->willReturn('taxon_position_mugs'); + + $this->retrieveData([ + 'order_by' => 'taxon_position', + 'sort' => 'desc', + ])->shouldBeEqualTo([ + 'sort' => [ + 'taxon_position_mugs' => [ + 'order' => SortDataHandlerInterface::SORT_DESC_INDEX, + 'unmapped_type' => 'keyword', + ], + ], + ]); + } + + function it_still_resolves_the_price_field_per_channel( + ChannelContextInterface $channelContext, + ChannelInterface $channel, + ConcatedNameResolverInterface $channelPricingNameResolver, + ): void { + $channelContext->getChannel()->willReturn($channel); + $channel->getCode()->willReturn('WEB'); + $channelPricingNameResolver->resolvePropertyName('WEB')->willReturn('price_WEB'); + + $this->retrieveData([ + 'order_by' => 'price', + 'sort' => 'asc', + ])->shouldBeEqualTo([ + 'sort' => [ + 'price_WEB' => [ + 'order' => SortDataHandlerInterface::SORT_ASC_INDEX, + 'unmapped_type' => 'keyword', + ], + ], + ]); + } + + function it_throws_an_exception_for_an_unsupported_sorter(): void + { + $this->shouldThrow(\UnexpectedValueException::class)->during('retrieveData', [ + ['order_by' => 'unsupported'], + ]); + } +} diff --git a/src/Controller/RequestDataHandler/ShopProductsSortDataHandler.php b/src/Controller/RequestDataHandler/ShopProductsSortDataHandler.php index 87378153..fdc4d105 100644 --- a/src/Controller/RequestDataHandler/ShopProductsSortDataHandler.php +++ b/src/Controller/RequestDataHandler/ShopProductsSortDataHandler.php @@ -16,14 +16,14 @@ use Sylius\Component\Channel\Context\ChannelContextInterface; use UnexpectedValueException; -final class ShopProductsSortDataHandler implements SortDataHandlerInterface +class ShopProductsSortDataHandler implements SortDataHandlerInterface { public function __construct( - private ConcatedNameResolverInterface $channelPricingNameResolver, - private ChannelContextInterface $channelContext, - private string $soldUnitsProperty, - private string $createdAtProperty, - private string $pricePropertyPrefix + protected ConcatedNameResolverInterface $channelPricingNameResolver, + protected ChannelContextInterface $channelContext, + protected string $soldUnitsProperty, + protected string $createdAtProperty, + protected string $pricePropertyPrefix ) { } @@ -31,24 +31,43 @@ public function retrieveData(array $requestData): array { $data = []; - $orderBy = $requestData[self::ORDER_BY_INDEX] ?? $this->createdAtProperty; + $orderBy = $requestData[self::ORDER_BY_INDEX] ?? $this->getDefaultOrderBy(); $sort = $requestData[self::SORT_INDEX] ?? self::SORT_ASC_INDEX; - $availableSorters = [$this->soldUnitsProperty, $this->createdAtProperty, $this->pricePropertyPrefix]; $availableSorting = [self::SORT_ASC_INDEX, self::SORT_DESC_INDEX]; - if (!in_array($orderBy, $availableSorters, true) || !in_array($sort, $availableSorting, true)) { + if (!in_array($orderBy, $this->getAvailableSorters(), true) || !in_array($sort, $availableSorting, true)) { throw new UnexpectedValueException(); } + $orderBy = $this->resolveOrderByProperty($orderBy); + + $data['sort'] = [$orderBy => ['order' => strtolower($sort), 'unmapped_type' => 'keyword']]; + + return $data; + } + + protected function getDefaultOrderBy(): string + { + return $this->createdAtProperty; + } + + /** + * @return string[] + */ + protected function getAvailableSorters(): array + { + return [$this->soldUnitsProperty, $this->createdAtProperty, $this->pricePropertyPrefix]; + } + + protected function resolveOrderByProperty(string $orderBy): string + { if ($this->pricePropertyPrefix === $orderBy) { /** @var string $channelCode */ $channelCode = $this->channelContext->getChannel()->getCode(); $orderBy = $this->channelPricingNameResolver->resolvePropertyName($channelCode); } - $data['sort'] = [$orderBy => ['order' => strtolower($sort), 'unmapped_type' => 'keyword']]; - - return $data; + return $orderBy; } } diff --git a/src/Controller/RequestDataHandler/TaxonProductsSortDataHandler.php b/src/Controller/RequestDataHandler/TaxonProductsSortDataHandler.php new file mode 100644 index 00000000..71350480 --- /dev/null +++ b/src/Controller/RequestDataHandler/TaxonProductsSortDataHandler.php @@ -0,0 +1,61 @@ +taxonPositionPropertyPrefix; + } + + protected function getAvailableSorters(): array + { + return array_merge(parent::getAvailableSorters(), [$this->taxonPositionPropertyPrefix]); + } + + protected function resolveOrderByProperty(string $orderBy): string + { + if ($this->taxonPositionPropertyPrefix === $orderBy) { + /** @var string $taxonCode */ + $taxonCode = $this->taxonContext->getTaxon()->getCode(); + + return $this->taxonPositionNameResolver->resolvePropertyName($taxonCode); + } + + return parent::resolveOrderByProperty($orderBy); + } +} diff --git a/templates/Shop/TaxonProductsSearch/content/body/main/sorting.html.twig b/templates/Shop/TaxonProductsSearch/content/body/main/sorting.html.twig index b7e3eee9..9460ae4f 100644 --- a/templates/Shop/TaxonProductsSearch/content/body/main/sorting.html.twig +++ b/templates/Shop/TaxonProductsSearch/content/body/main/sorting.html.twig @@ -4,7 +4,9 @@ {% set route_parameters = app.request.query.all|unset_elements(['order_by', 'sort', 'page']) %} {% if app.request.query.all()['order_by'] is not defined or app.request.query.all()['order_by'] is empty %} - {% set current_sorting_label = 'bitbag_sylius_elasticsearch_plugin.ui.newest'|trans|lower %} + {% set current_sorting_label = 'bitbag_sylius_elasticsearch_plugin.ui.position'|trans|lower %} + {% elseif app.request.query.all()['order_by'] == 'taxon_position' %} + {% set current_sorting_label = 'bitbag_sylius_elasticsearch_plugin.ui.position'|trans|lower %} {% elseif app.request.query.all()['order_by'] == 'sold_units'%} {% set current_sorting_label = 'bitbag_sylius_elasticsearch_plugin.ui.bestsellers'|trans|lower %} {% elseif app.request.query.all()['order_by'] == 'product_created_at' and app.request.query.all()['sort'] == 'desc'%} diff --git a/translations/messages.en.yml b/translations/messages.en.yml index efe67b72..081b4aca 100644 --- a/translations/messages.en.yml +++ b/translations/messages.en.yml @@ -5,6 +5,7 @@ bitbag_sylius_elasticsearch_plugin: filter_results: Filter results filter: Filter sort: Sort by + position: Position bestsellers: Bestsellers newest: Newest oldest: Oldest