Skip to content

Commit

Permalink
Merge pull request #169 from delyriand/doc/sorter-doc-example
Browse files Browse the repository at this point in the history
Sorter documentation and example
  • Loading branch information
maximehuran authored Jul 25, 2023
2 parents 17ae28d + 8145faa commit b89e02c
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 2 deletions.
23 changes: 23 additions & 0 deletions dist/config/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
locale: en_US

services:
# Default configuration for services in *this* file
_defaults:
# Automatically injects dependencies in your services
autowire: true

# Automatically registers your services as commands, event subscribers, etc.
autoconfigure: true

# Allows optimizing the container by removing unused services; this also means
# fetching services directly from the container via $container->get() won't work
public: false

_instanceof:
Sylius\Bundle\ResourceBundle\Controller\ResourceController:
autowire: false
Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType:
autowire: false
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ mappings:
properties:
short_description:
type: text
fields:
keyword: # add keyword field for sorting
type: keyword
19 changes: 18 additions & 1 deletion dist/src/Resources/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,19 @@ services:
autowire: true
autoconfigure: true
public: false


# Makes classes in src/ available to be used as services;
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../../*'
exclude: '../../{Entity,Migrations,Tests,Kernel.php}'

# Controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../../Controller'
tags: ['controller.service_arguments']

# Add short_description in indexed data with the logic to get the value
App\Search\Automapper\DecorateProductMapperConfiguration:
decorates: MonsieurBiz\SyliusSearchPlugin\AutoMapper\ProductMapperConfiguration
Expand All @@ -27,3 +39,8 @@ services:
- 'description'
- 'name.autocomplete'
- 'short_description' # We add our new value in instant search

# Define sorters
App\Search\Request\Sorting\Product\ShortDescriptionSorter:
tags:
- { name: monsieurbiz.search.request.product_sorter }
34 changes: 34 additions & 0 deletions dist/src/Search/Request/Sorting/Product/ShortDescriptionSorter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/*
* This file is part of Monsieur Biz' Search plugin for Sylius.
*
* (c) Monsieur Biz <[email protected]>
*
* For the full copyright and license information, please view the LICENSE.txt
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace App\Search\Request\Sorting\Product;

use Elastica\Query;
use MonsieurBiz\SyliusSearchPlugin\Search\Request\RequestConfiguration;
use MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\SorterBuilderTrait;
use MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\SorterInterface;

class ShortDescriptionSorter implements SorterInterface
{
use SorterBuilderTrait;

public function apply(Query $query, RequestConfiguration $requestConfiguration): void
{
$sorting = $requestConfiguration->getSorting();
if (!\array_key_exists('short_description', $sorting)) {
return;
}

$query->addSort($this->buildSort('short_description.keyword', $sorting['short_description']));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{% if result.count > 0 %}
{% set route = app.request.attributes.get('_route') %}
{% set route_parameters = app.request.attributes.get('_route_params')|merge(app.request.query.all) %}

{% set criteria = app.request.query.all()['criteria']|default({}) %}

{% set default_path = path(route, route_parameters|merge({'sorting': null, 'criteria': criteria})) %}
{% set from_a_to_z_path = path(route, route_parameters|merge({'sorting': {'name': 'asc'}, 'criteria': criteria})) %}
{% set from_z_to_a_path = path(route, route_parameters|merge({'sorting': {'name': 'desc'}, 'criteria': criteria})) %}
{% set oldest_first_path = path(route, route_parameters|merge({'sorting': {'created_at': 'asc'}, 'criteria': criteria})) %}
{% set newest_first_path = path(route, route_parameters|merge({'sorting': {'created_at': 'desc'}, 'criteria': criteria})) %}
{% set cheapest_first_path = path(route, route_parameters|merge({'sorting': {'price': 'asc'}, 'criteria': criteria})) %}
{% set most_expensive_first_path = path(route, route_parameters|merge({'sorting': {'price': 'desc'}, 'criteria': criteria})) %}
{% set short_desc_a_to_z_path = path(route, route_parameters|merge({'sorting': {'short_description': 'asc'}, 'criteria': criteria})) %}
{% set short_desc_z_to_a_path = path(route, route_parameters|merge({'sorting': {'short_description': 'desc'}, 'criteria': criteria})) %}

{% set sorting = app.request.query.all()['sorting']|default() %}
{% if sorting is empty %}
{% set current_sorting_label = 'sylius.ui.by_position'|trans|lower %}
{% elseif sorting.name is defined and sorting.name == 'asc'%}
{% set current_sorting_label = 'sylius.ui.from_a_to_z'|trans|lower %}
{% elseif sorting.name is defined and sorting.name == 'desc'%}
{% set current_sorting_label = 'sylius.ui.from_z_to_a'|trans|lower %}
{% elseif sorting.created_at is defined and sorting.created_at == 'desc'%}
{% set current_sorting_label = 'sylius.ui.newest_first'|trans|lower %}
{% elseif sorting.created_at is defined and sorting.created_at == 'asc'%}
{% set current_sorting_label = 'sylius.ui.oldest_first'|trans|lower %}
{% elseif sorting.price is defined and sorting.price == 'asc'%}
{% set current_sorting_label = 'sylius.ui.cheapest_first'|trans|lower %}
{% elseif sorting.price is defined and sorting.price == 'desc' %}
{% set current_sorting_label = 'sylius.ui.most_expensive_first'|trans|lower %}
{% elseif sorting.short_description is defined and sorting.short_description == 'asc' %}
{% set current_sorting_label = 'app.ui.short_desc_a_to_z_path'|trans|lower %}
{% elseif sorting.short_description is defined and sorting.short_description == 'desc' %}
{% set current_sorting_label = 'app.ui.short_desc_z_to_a_path'|trans|lower %}
{% else %}
{% set current_sorting_label = 'sylius.ui.by_position'|trans|lower %}
{% endif %}

<div class="ui right floated small header">
<div class="content">
{{ 'sylius.ui.sort'|trans }}
<div class="ui inline dropdown">
<div class="text">{{ current_sorting_label }}</div>
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{ default_path }}" data-text="{{ 'sylius.ui.by_position'|trans|lower }}">{{ 'sylius.ui.by_position'|trans }}</a>
<a class="item" href="{{ from_a_to_z_path }}" data-text="{{ 'sylius.ui.from_a_to_z'|trans|lower }}">{{ 'sylius.ui.from_a_to_z'|trans }}</a>
<a class="item" href="{{ from_z_to_a_path }}" data-text="{{ 'sylius.ui.from_z_to_a'|trans|lower }}">{{ 'sylius.ui.from_z_to_a'|trans }}</a>
<a class="item" href="{{ newest_first_path }}" data-text="{{ 'sylius.ui.newest_first'|trans|lower }}">{{ 'sylius.ui.newest_first'|trans }}</a>
<a class="item" href="{{ oldest_first_path }}" data-text="{{ 'sylius.ui.oldest_first'|trans|lower }}">{{ 'sylius.ui.oldest_first'|trans }}</a>
<a class="item" href="{{ cheapest_first_path }}" data-text="{{ 'sylius.ui.cheapest_first'|trans|lower }}">{{ 'sylius.ui.cheapest_first'|trans }}</a>
<a class="item" href="{{ most_expensive_first_path }}" data-text="{{ 'sylius.ui.most_expensive_first'|trans|lower }}">{{ 'sylius.ui.most_expensive_first'|trans }}</a>
<a class="item" href="{{ short_desc_a_to_z_path }}" data-text="{{ 'sylius.ui.from_a_to_z'|trans|lower }}">{{ 'app.ui.short_desc_a_to_z_path'|trans }}</a>
<a class="item" href="{{ short_desc_z_to_a_path }}" data-text="{{ 'sylius.ui.from_a_to_z'|trans|lower }}">{{ 'app.ui.short_desc_z_to_a_path'|trans }}</a>
</div>
</div>
</div>
</div>
{% endif %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{% import "@SyliusShop/Common/Macro/money.html.twig" as money %}

<div class="ui fluid card">
<a href="{{ path('sylius_shop_product_show', {'slug': item.slug, '_locale': sylius.localeCode}) }}" class="blurring dimmable image">
<div class="ui dimmer">
<div class="content">
<div class="center">
<div class="ui inverted button">{{ 'sylius.ui.view_more'|trans }}</div>
</div>
</div>
</div>
{% if item.images|first %}
{% set path = item.images|first.path|imagine_filter(filter|default('sylius_shop_product_thumbnail')) %}
{% else %}
{% set path = '//placehold.it/200x200' %}
{% endif %}

<img src="{{ path }}" alt="{{ item.name }}" class="ui bordered image" />
</a>
<div class="content">
<a href="{{ path('sylius_shop_product_show', {'slug': item.slug, '_locale': sylius.localeCode}) }}" class="header sylius-product-name">{{ item.name }}</a>

<div class="description">{{ item.short_description }}</div>

{% if item.prices is not empty %}
{% set pricing = item.prices|filter(price => price.channelCode == sylius.channel.code)|first %}
<div class="sylius-product-price">{{ money.convertAndFormat(pricing.price) }}</div>
{% endif %}
</div>
</div>
4 changes: 4 additions & 0 deletions dist/translations/messages.en.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
app:
ui:
short_desc_a_to_z_path: From short description A to Z
short_desc_z_to_a_path: From short description Z to A
33 changes: 32 additions & 1 deletion docs/add_custom_sorts.md
Original file line number Diff line number Diff line change
@@ -1 +1,32 @@
@TODO
# Add custom sorts

## Create your own sorter service

You can create your own sorter service by implementing the `SorterInterface` interface.
For example, in your test application, [we have a short description sorter](../dist/src/Search/Request/Sorting/Product/ShortDescriptionSorter.php).

Add [the tag `monsieurbiz.search.request.product_sorter` to your service](../dist/src/Resources/config/services.yaml#L32).

## Replace existing sorter

In your `services.yaml` file, you can replace the existing sorter by your own service.

```yaml
services:
MonsieurBiz\SyliusSearchPlugin\Search\Request\Sorting\Product\PriceSorter:
class: App\Search\Request\Sorting\Product\PriceSorter
tags:
- { name: monsieurbiz.search.request.product_sorter }
```
Don't forget to create your class!
If you want to keep the logic but add code before and/or after, you can also [decorate the service](https://symfony.com/doc/current/service_container/service_decoration.html).
## Display the sorter in the front
Override the [sorting.html.twig template](../dist/templates/bundles/MonsieurBizSyliusSearchPlugin/Search/_sorting.html.twig) to add your sort.
## Tips
A text field can't be used for sorting. In this case, you can [create a "keyword" subfield](../dist/src/Resources/config/elasticsearch/monsieurbiz_product_mapping.yaml#L5).

0 comments on commit b89e02c

Please sign in to comment.