Skip to content

Implement individual sorting

Implement individual sorting

INFO

This document represents an architecture decision record (ADR) and has been mirrored from the ADR section in our Shopware 6 repository. You can find the original version here

Context

Shop owners should be able to define custom sorting options for product listings and search result pages out of the administration. It should be possible to define a system default sorting option for product listings. Top Results will be the default on search pages and suggest route, which sorts products by _score.

Currently, to define a custom sorting option, you need to define it as a service and tag it as shopware.sales_channel.product_listing.sorting. This is somewhat tedious and makes it impossible to define individual sortings via the administration.

Decision

From now on, it is possible to define custom sortings via the administration.

Individual sortings will be stored in the database in the table product_sorting and its translatable label in the product_sorting_translation table.

It is possible to define a system default product listing sorting option, which is stored in system_default.core.listing.defaultSorting. This however has no influence on the default Top Results sorting on search pages and the suggest route result.

To define custom sorting options via a plugin, you can either write a migration to store them in the database. This method is recommended, as the sortings can be managed via the administration.

The product_sorting table looks like the following:

ColumnTypeNotes
idbinary(16)
url_keyvarchar(255)Key (unique). Shown in url, when sorting is chosen
priorityint unsignedHigher priority means, the sorting will be sorted top
activetinyint(1) [1]Inactive sortings will not be shown and will not sort
lockedtinyint(1) [0]Locked sortings can not be edited via the DAL
fieldsjsonJSON of the fields by which to sort the listing
created_atdatetime(3)
updated_atdatetime(3)

The JSON for the fields column look like this:

json5
[
  {
    "field": "product.name",        // property to sort by (mandatory)  
    "order": "desc",                // "asc" or "desc" (mandatory)
    "priority": 0,                  // in which order the sorting is to applied (higher priority comes first) (mandatory)
    "naturalSorting": 0
  },
  {
    "field": "product.cheapestPrice",
    "order": "asc",
    "priority": 100,
    "naturalSorting": 0
  },
  // ...
]

Otherwise, you can subscribe to the ProductListingCriteriaEvent to add a ProductSortingEntity as available sorting on the fly.

php
<?php

namespace Shopware\Core\Content\Product\SalesChannel\Sorting\Example;

use Shopware\Core\Content\Product\Events\ProductListingCriteriaEvent;
use Shopware\Core\Content\Product\SalesChannel\Sorting\ProductSortingCollection;
use Shopware\Core\Content\Product\SalesChannel\Sorting\ProductSortingEntity;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class ExampleListingSubscriber implements EventSubscriberInterface {

    public static function getSubscribedEvents(): array
    {
        return [
            ProductListingCriteriaEvent::class => ['addMyCustomSortingToStorefront', 500],
        ];
    }

    public function addMyCustomSortingToStorefront(ProductListingCriteriaEvent $event): void 
    {
        /** @var ProductSortingCollection $availableSortings */
        $availableSortings = $event->getCriteria()->getExtension('sortings') ?? new ProductSortingCollection();
        
        $myCustomSorting = new ProductSortingEntity();
        $myCustomSorting->setId(Uuid::randomHex());
        $myCustomSorting->setActive(true);
        $myCustomSorting->setTranslated(['label' => 'My Custom Sorting']);
        $myCustomSorting->setKey('my-custom-sort');
        $myCustomSorting->setPriority(5);
        $myCustomSorting->setFields([
            [
                'field' => 'product.name',
                'order' => 'desc',
                'priority' => 1,
                'naturalSorting' => 0,
            ],
        ]);
        
        $availableSortings->add($myCustomSorting);
        
        $event->getCriteria()->addExtension('sortings', $availableSortings);
    }
}

Consequences

The old behaviour of defining the custom sorting as a tagged service is deprecated and will be removed in v6.4.0.