Search code examples
sortingshopware

Sorting by reference price in Shopware


From the official documentation https://developer.shopware.com/docs/guides/plugins/plugins/storefront/add-custom-sorting-product-listing.html it is clear enough how to add new custom sorting on PLP with Shopware. But how to implement sorting by the reference price? This value is not stored in the database but calculated on fly, thus it is not possible to provide a field parameter at $myCustomSorting->setFields().

For example, we have two products with gross prices 10.00 and 15.00 and these products have selling units 0.5 and 0.9 respectively (for m2 in pack). We want to sort products based on reference price per m2 10 / 0.5 = 20.00 and 15 / 0.9 = 16.67.


Solution

  • In the end, the problem was fixed with the following solution:

    • create the new UnitPrice field class which extends Shopware\Core\Content\Product\DataAbstractionLayer\CheapestPrice\CheapestPriceField:
    class UnitPriceField extends CheapestPriceField
    {
        protected function getAccessorBuilderClass(): ?string
        {
            return UnitPriceFieldAccessorBuilder::class;
        }
    }
    
    • create the related UnitPriceFieldAccessorBuilder class which also extends Shopware\Core\Content\Product\DataAbstractionLayer\CheapestPrice\CheapestPriceAccessorBuilder and recalculates result taking into account referenceUnit and purchaseUnit values:
    class UnitPriceFieldAccessorBuilder extends CheapestPriceAccessorBuilder
    {
        public function buildAccessor(string $root, Field $field, Context $context, string $accessor): ?string
        {
            $price = parent::buildAccessor($root, $field, $context, 'cheapestPrice');
    
            return $price . ' * IFNULL(`product`.`reference_unit`, 1) / IFNULL(`product`.`purchase_unit`, 1)';
        }
    }
    
    • decorate the Shopware\Core\Content\Product\SalesChannel\SalesChannelProductDefinition class to add new field:
    class SalesChannelProductDefinition extends ShopwareSalesChannelProductDefinition
    {
        protected function defineFields(): FieldCollection
        {
            $fields = parent::defineFields();
    
            $fields->add(
                (new UnitPriceField('unit_price', 'unitPrice'))
                    ->addFlags(new Runtime())
            );
    
            return $fields;
        }
    }
    
    • update the product_sorting table for url-keys price-asc/price-desc to use field product.unitPrice instead of product.cheapestPrice. Alternatively, just add a new sorting row here.