integration_documentation:plugin:en:integration:shopware_6:extension

integration_documentation:plugin:en:integration:shopware_6:extension

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
integration_documentation:plugin:en:integration:shopware_6:extension [2022/06/21 09:36]
tobi
integration_documentation:plugin:en:integration:shopware_6:extension [2022/12/13 13:42] (current)
tobi
Line 15: Line 15:
  
 </​markdown>​ </​markdown>​
-<note tip>​Please make sure to use the same major version as the base Findologic plugin. This means that ''​2.x''​ is compatible with ''​2.x''​ and ''​3.x''​ is compatible with ''​3.x'',​ etc.</​note>​+<note tip>​Please make sure to use the same major version as the base Findologic plugin. This means that ''​3.x''​ is compatible with ''​3.x''​ and ''​4.x''​ is compatible with ''​4.x'',​ etc.</​note>​
 <​markdown>​ <​markdown>​
  
Line 25: Line 25:
  
 By default the extension plugin decorates the `AttributeAdapter` and `DefaultPropertiesAdapter`,​ which are responsible to generate the attributes and properties of a product. By default the extension plugin decorates the `AttributeAdapter` and `DefaultPropertiesAdapter`,​ which are responsible to generate the attributes and properties of a product.
-Any adapter in `FINDOLOGIC\FinSearch\Export\Adapters` can be decorated.+Any adapter in `FINDOLOGIC\Shopware6Common\Export\Adapters` can be decorated. The original files are located within `vendor/​findologic/​shopware6-common/​src/​Export/​Adapters`.
  
 `src/​Resources/​config/​services.xml` `src/​Resources/​config/​services.xml`
Line 38: Line 38:
         <service         <service
             id="​FINDOLOGIC\ExtendFinSearch\Export\Adapters\AttributeAdapter"​             id="​FINDOLOGIC\ExtendFinSearch\Export\Adapters\AttributeAdapter"​
-            decorates="​FINDOLOGIC\FinSearch\Export\Adapters\AttributeAdapter"​+            decorates="​FINDOLOGIC\Shopware6Common\Export\Adapters\AttributeAdapter"​
             public="​true"​             public="​true"​
             decoration-on-invalid="​ignore"​             decoration-on-invalid="​ignore"​
 +            autowire="​true"​
         >         >
-            <​argument ​type="service" ​id="​service_container"​ /> +            <​argument ​key="$dynamicProductGroupService" type="​service"​ id="​FINDOLOGIC\FinSearch\Export\Services\DynamicProductGroupService" /> 
-            <​argument ​type="​service"​ id="​FINDOLOGIC\FinSearch\Struct\Config" /> +            <​argument ​key="​$catUrlBuilderService" ​type="​service"​ id="FINDOLOGIC\FinSearch\Export\Services\CatUrlBuilderService" /> 
-            <​argument type="​service"​ id="Shopware\Core\Framework\Adapter\Translation\Translator" /> +            <​argument ​key="$translator" type="​service"​ id="Shopware\Core\Framework\Adapter\Translation\Translator" />
-            <​argument ​type="service" ​id="​fin_search.sales_channel_context"​ /> +
-            <​argument ​type="​service"​ id="FINDOLOGIC\FinSearch\Export\UrlBuilderService"​ /> +
-            <​argument type="​service"​ id="​fin_search.export_context" />+
         </​service>​         </​service>​
  
         <service         <service
             id="​FINDOLOGIC\ExtendFinSearch\Export\Adapters\DefaultPropertiesAdapter"​             id="​FINDOLOGIC\ExtendFinSearch\Export\Adapters\DefaultPropertiesAdapter"​
-            decorates="​FINDOLOGIC\FinSearch\Export\Adapters\DefaultPropertiesAdapter"​+            decorates="​FINDOLOGIC\Shopware6Common\Export\Adapters\DefaultPropertiesAdapter"​
             public="​true"​             public="​true"​
             decoration-on-invalid="​ignore"​             decoration-on-invalid="​ignore"​
 +            autowire="​true"​
         >         >
-            <​argument ​type="service" ​id="​FINDOLOGIC\FinSearch\Struct\Config"​ /> +            <​argument ​key="$translator" type="​service"​ id="​Shopware\Core\Framework\Adapter\Translation\Translator"​ />
-            <​argument type="​service"​ id="​fin_search.sales_channel_context"​ /> +
-            <​argument ​type="​service"​ id="​Shopware\Core\Framework\Adapter\Translation\Translator"​ />+
         </​service>​         </​service>​
  
Line 78: Line 75:
  
 use FINDOLOGIC\Export\Data\Attribute;​ use FINDOLOGIC\Export\Data\Attribute;​
-use FINDOLOGIC\FinSearch\Export\Adapters\AttributeAdapter as OriginalAttributeAdapter;​ +use FINDOLOGIC\Shopware6Common\Export\Adapters\AttributeAdapter as OriginalAttributeAdapter;​ 
-use Shopware\Core\Content\Product\ProductEntity;​+use Vin\ShopwareSdk\Data\Entity\Product\ProductEntity;​
  
 class AttributeAdapter extends OriginalAttributeAdapter class AttributeAdapter extends OriginalAttributeAdapter
Line 110: Line 107:
  
 use FINDOLOGIC\Export\Data\Property;​ use FINDOLOGIC\Export\Data\Property;​
-use FINDOLOGIC\FinSearch\Export\Adapters\DefaultPropertiesAdapter as OriginalDefaultPropertiesAdapter;​ +use FINDOLOGIC\Shopware6Common\Export\Adapters\DefaultPropertiesAdapter as OriginalDefaultPropertiesAdapter;​ 
-use Shopware\Core\Content\Product\ProductEntity;​+use Vin\ShopwareSdk\Data\Entity\Product\ProductEntity;​
  
 class DefaultPropertiesAdapter extends OriginalDefaultPropertiesAdapter class DefaultPropertiesAdapter extends OriginalDefaultPropertiesAdapter
Line 197: Line 194:
 This data structure is exactly like Shopware determines its results on listing pages. The used main variant in this case is typically the cheapest available variant. In case a product has no variants, or [fan out properties in the product list](https://​docs.shopware.com/​en/​shopware-6-en/​catalogues/​products?​category=shopware-6-en/​catalogues#​variants) is configured, the product has its own display group, so none of this reordering is happening. This data structure is exactly like Shopware determines its results on listing pages. The used main variant in this case is typically the cheapest available variant. In case a product has no variants, or [fan out properties in the product list](https://​docs.shopware.com/​en/​shopware-6-en/​catalogues/​products?​category=shopware-6-en/​catalogues#​variants) is configured, the product has its own display group, so none of this reordering is happening.
  
-### Get variant data+### Basic code structure 
 + 
 +This extension will use the defined events from the main plugin. 
 + 
 +`src/​Subscriber/​ProductSubscriber.php` 
 +```php 
 +<?php 
 + 
 +declare(strict_types=1);​ 
 + 
 +namespace FINDOLOGIC\ExtendFinSearch\Subscriber;​ 
 + 
 +use FINDOLOGIC\Export\Data\Property;​ 
 +use FINDOLOGIC\Shopware6Common\Export\Adapters\AdapterFactory;​ 
 +use FINDOLOGIC\Shopware6Common\Export\Events\AfterItemBuildEvent;​ 
 +use FINDOLOGIC\Shopware6Common\Export\Events\AfterVariantAdaptEvent;​ 
 +use FINDOLOGIC\Shopware6Common\Export\Events\BeforeItemAdaptEvent;​ 
 +use Symfony\Component\EventDispatcher\EventSubscriberInterface;​ 
 +use Vin\ShopwareSdk\Data\Entity\Product\ProductEntity;​ 
 + 
 +class ProductSubscriber implements EventSubscriberInterface 
 +
 +    /** @var AdapterFactory */ 
 +    private $adapterFactory;​ 
 + 
 +    private $variantData = []; 
 + 
 +    public function __construct(AdapterFactory $adapterFactory) 
 +    { 
 +        $this->​adapterFactory = $adapterFactory;​ 
 +    } 
 + 
 +    public static function getSubscribedEvents() 
 +    { 
 +        return [ 
 +            AfterVariantAdaptEvent::​NAME => '​afterVariantAdapted',​ 
 +            AfterItemBuildEvent::​NAME => '​afterItemCompleted'​ 
 +        ]; 
 +    } 
 + 
 +    public function afterVariantAdapted(AfterVariantAdaptEvent $event) 
 +    { 
 +        $product = $event->​getProduct();​ 
 + 
 +        $this->​variantData[] = [ 
 +            // Your variant data 
 +        ]; 
 +    } 
 + 
 +    public function afterItemCompleted(AfterItemBuildEvent $event) 
 +    { 
 +        $item = $event->​getItem();​ 
 + 
 +        if (count($this->​variantData)) { 
 +            $item->​addProperty( 
 +                new Property('​variants',​ [ 
 +                    ''​ => json_encode($this->​variantData) 
 +                ]) 
 +            ); 
 +        } 
 +         
 +        $this->​variantData = []; 
 +    } 
 +
 +``` 
 + 
 +`src/​Resources/​config/​services.xml` 
 +```xml 
 +<?xml version="​1.0"​ ?> 
 +<​container xmlns:​xsi="​http://​www.w3.org/​2001/​XMLSchema-instance"​ 
 +    xmlns="​http://​symfony.com/​schema/​dic/​services"​ 
 +    xsi:​schemaLocation="​http://​symfony.com/​schema/​dic/​services http://​symfony.com/​schema/​dic/​services/​services-1.0.xsd">​ 
 + 
 +    <​services>​ 
 + 
 +        <​service 
 +            id="​FINDOLOGIC\ExtendFinSearch\Subscriber\ProductSubscriber"​ 
 +            class="​FINDOLOGIC\ExtendFinSearch\Subscriber\ProductSubscriber"​ 
 +            public="​true"​ 
 +        > 
 +            <tag name="​kernel.event_subscriber"​ /> 
 +            <​argument type="​service"​ id="​FINDOLOGIC\Shopware6Common\Export\Adapters\AdapterFactory"​ /> 
 +        </​service>​ 
 + 
 +    </​services>​ 
 +</​container>​ 
 +``` 
 + 
 +### Add basic variant data 
 + 
 +`src/​Subscriber/​ProductSubscriber.php` 
 +```php 
 +//... 
 + 
 +public function afterVariantAdapted(AfterVariantAdaptEvent $event) 
 +
 +    $product = $event->​getProduct();​ 
 + 
 +    $this->​variantData[] = [ 
 +        '​name'​ => $this->​getName($product),​ 
 +        '​url'​ => $this->​getUrl($product),​ 
 +        '​price'​ => $this->​getPrice($product) 
 +    ]; 
 +
 + 
 +//... 
 + 
 +private function getName(ProductEntity $productEntity):​ string 
 +
 +    $name = $this->​adapterFactory->​getNameAdapter()->​adapt($productEntity);​ 
 + 
 +    return $name ? $name->​getValues()[''​] : '';​ 
 +
 + 
 +private function getUrl(ProductEntity $productEntity):​ string 
 +
 +    $url = $this->​adapterFactory->​getUrlAdapter()->​adapt($productEntity);​ 
 + 
 +    return $url ? $url->​getValues()[''​] : '';​ 
 +
 + 
 +private function getPrice(ProductEntity $productEntity):​ string 
 +
 +    $prices = $this->​adapterFactory->​getPriceAdapter()->​adapt($productEntity);​ 
 + 
 +    $prices = array_filter($prices,​ function (Price $price) { 
 +        return array_key_exists('',​ $price->​getValues());​ 
 +    }); 
 + 
 +    return count($prices) ? current($prices)->​getValues()[''​] : '';​ 
 +
 + 
 +//... 
 +``` 
 + 
 +This will export the `name`, `url` and `price` of each variant, the JSON would be like:  
 +```json 
 +
 +  { 
 +    "​name":​ "​Findologic T-Shirt (Gray - Orange Logo)",​ 
 +    "​url":​ "​https://​store.com/​Findologic-T-Shirt-Gray-Orange-Logo/​411dc735cade4c8789421f9c2aaec51f",​ 
 +    "​price":​ 59.99 
 +  }, 
 +  { 
 +    "​name":​ "​Findologic T-Shirt",​ 
 +    "​url":​ "​https://​store.com/​Findologic-T-Shirt/​ad99a6257e1546f08dbe9886a48e4230",​ 
 +    "​price":​ 55.99 
 +  }, 
 +  { 
 +    "​name":​ "​Findologic T-Shirt (Black - White Logo)",​ 
 +    "​url":​ "​https://​store.com/​Findologic-T-Shirt/​7c18c5ff8aa548e1bdba6b738ac42f71",​ 
 +    "​price":​ 49.99 
 +  } 
 +
 +``` 
 + 
 +### Add images 
 + 
 +For variant images, you will need to add the relevant variant associations as shown [here](#​add_product_or_variant_associations). (`cover` and `media`) 
 + 
 +Additionally:​ 
 +`src/​Subscriber/​ProductSubscriber.php` 
 +```php 
 +// ... 
 + 
 +public function afterVariantAdapted(AfterVariantAdaptEvent $event) 
 +
 +    $product = $event->​getProduct();​ 
 + 
 +    $this->​variantData[] = [ 
 +        // ... 
 +        '​image'​ => $this->​getImageUrl($product) 
 +    ]; 
 +
 + 
 +// ... 
 + 
 +private function getImageUrl(ProductEntity $productEntity):​ string 
 +
 +    $images = $this->​adapterFactory->​getImagesAdapter()->​adapt($productEntity);​ 
 + 
 +    return count($images) ? current($images)->​getUrl() : '';​ 
 +
 +``` 
 + 
 +### Deal with variant-specific data 
 + 
 +`src/​Subscriber/​ProductSubscriber.php` 
 +```php 
 +// ... 
 + 
 +private const VARIANT_PROPERTY = '​color';​ 
 + 
 +//... 
 + 
 +public function afterVariantAdapted(AfterVariantAdaptEvent $event) 
 +
 +    $product = $event->​getProduct();​ 
 + 
 +    foreach ($product->​properties as $variantProperty) { 
 +        // Ignore all properties except the property we want to export our variants off. 
 +        if ( 
 +            !$variantProperty->​group || 
 +            $variantProperty->​group->​getTranslation('​name'​) !== self::​VARIANT_PROPERTY) 
 +        { 
 +            continue; 
 +        } 
 + 
 +        $propertyName = $variantProperty->​getTranslation('​name'​);​ 
 + 
 +        $this->​variantData[$propertyName] = [ 
 +            '​name'​ => $this->​getName($product),​ 
 +            '​url'​ => $this->​getUrl($product),​ 
 +            '​price'​ => $this->​getPrice($product),​ 
 +            '​image'​ => $this->​getImageUrl($product),​ 
 +            self::​VARIANT_PROPERTY => $propertyName 
 +        ]; 
 +    } 
 +
 + 
 +// ... 
 +``` 
 + 
 +After this implementation,​ the JSON would be like:  
 + 
 +```json 
 +
 +  "​Gray":​ { 
 +    "​name":​ "​Findologic T-Shirt (Gray - Orange Logo)",​ 
 +    "​image":​ "​https://​store.com/​some/​path/​to/​image_800x800.jpg",​ 
 +    "​url":​ "​https://​store.com/​Findologic-T-Shirt-Gray-Orange-Logo/​411dc735cade4c8789421f9c2aaec51f",​ 
 +    "​color":​ "​Gray"​ 
 +  }, 
 +  "​Black":​ { 
 +    "​name":​ "​Findologic T-Shirt (Black - White Logo)",​ 
 +    "​image":​ "​https://​store.com/​some/​path/​to/​image_800x800.jpg",​ 
 +    "​url":​ "​https://​store.com/​Findologic-T-Shirt/​7c18c5ff8aa548e1bdba6b738ac42f71",​ 
 +    "​color":​ "​Black"​ 
 +  } 
 +
 +``` 
 + 
 +### Usergroup-specific variants 
 + 
 +`src/​Subscriber/​ProductSubscriber.php` 
 +```php 
 +//... 
 + 
 +public function afterVariantAdapted(AfterVariantAdaptEvent $event) 
 +
 +    $product = $event->​getProduct();​ 
 + 
 +    $basicVariantData = [ 
 +        '​name'​ => $this->​getName($product),​ 
 +        '​url'​ => $this->​getUrl($product),​ 
 +        '​image'​ => $this->​getImageUrl($product) 
 +    ]; 
 + 
 +    foreach ($this->​adapterFactory->​getPriceAdapter()->​adapt($product) as $price) { 
 +        $values = $price->​getValues();​ 
 + 
 +        $variantData = array_merge( 
 +            $basicVariantData,​ 
 +            [ 
 +                '​price'​ => current($values) 
 +            ] 
 +        ); 
 + 
 +        $this->​variantData[array_key_first($values)][] = json_encode($variantData);​ 
 +    } 
 +
 + 
 +public function afterItemCompleted(AfterItemBuildEvent $event) 
 +
 +    $variantData = []; 
 +    foreach ($this->​variantData as $usergroup => $userGroupVariantData) { 
 +        $variantData[$usergroup] = json_encode($userGroupVariantData);​ 
 +    } 
 + 
 +    $item = $event->​getItem();​ 
 + 
 +    if (count($this->​variantData)) { 
 +        $item->​addProperty( 
 +            new Property('​variants',​ $variantData) 
 +        ); 
 +    } 
 +
 + 
 +//... 
 +``` 
 + 
 +</​markdown>​ 
 + 
 +<note tip>''​variants''​ **must** be exported for **all** usergroups. If a usergroup is not exported, this usergroup will not have any ''​variants''​ data.</​note>​ 
 + 
 +<​markdown>​ 
 + 
 +An XML export with usergroup in properties may look like: 
 + 
 +```xml 
 +<!-- ... --> 
 +<​allProperties>​ 
 +    <​property usergroup="​JyInVwMCBQtTdQ1RAwkmCSRXUVkGBCZ6B1YgDyFXVnc=">​ 
 +        <​key>​ 
 +            <​![CDATA[ variants ]]> 
 +        </​key>​ 
 +        <​value>​ 
 +            <​![CDATA[ 
 +                [ 
 +                    { 
 +                        "​name":​ "​Findologic T-Shirt (Gray - Orange Logo)",​ 
 +                        "​image":​ "​https://​store.com/​some/​path/​to/​image_800x800.jpg",​ 
 +                        "​url":​ "​https://​store.com/​Findologic-T-Shirt-Gray-Orange-Logo/​411dc735cade4c8789421f9c2aaec51f",​ 
 +                        "​price":​ 59.99 
 +                    }, 
 +                    { 
 +                        "​name":​ "​Findologic T-Shirt",​ 
 +                        "​image":​ "​https://​store.com/​some/​path/​to/​image_800x800.jpg",​ 
 +                        "​url":​ "​https://​store.com/​Findologic-T-Shirt/​ad99a6257e1546f08dbe9886a48e4230",​ 
 +                        "​price":​ 55.99 
 +                    }, 
 +                    { 
 +                        "​name":​ "​Findologic T-Shirt (Black - White Logo)",​ 
 +                        "​image":​ "​https://​store.com/​some/​path/​to/​image_800x800.jpg",​ 
 +                        "​url":​ "​https://​store.com/​Findologic-T-Shirt/​7c18c5ff8aa548e1bdba6b738ac42f71",​ 
 +                        "​price":​ 49.99 
 +                    } 
 +                ] 
 +            ]]> 
 +        </​value>​ 
 +    </​property>​ 
 +    <​property usergroup="​dX1WUgsgCyVUIHcPcXYFUw1xUlBZUwcAB1FUcgNQCwA=">​ 
 +        <​key>​ 
 +            <​![CDATA[ variants ]]> 
 +        </​key>​ 
 +        <​value>​ 
 +            <​![CDATA[ 
 +                [ 
 +                    { 
 +                        "​name":​ "​Findologic T-Shirt (Gray - Orange Logo)",​ 
 +                        "​image":​ "​https://​store.com/​some/​path/​to/​image_800x800.jpg",​ 
 +                        "​url":​ "​https://​store.com/​Findologic-T-Shirt-Gray-Orange-Logo/​411dc735cade4c8789421f9c2aaec51f",​ 
 +                        "​price":​ 599.99 
 +                    }, 
 +                    { 
 +                        "​name":​ "​Findologic T-Shirt",​ 
 +                        "​image":​ "​https://​store.com/​some/​path/​to/​image_800x800.jpg",​ 
 +                        "​url":​ "​https://​store.com/​Findologic-T-Shirt/​ad99a6257e1546f08dbe9886a48e4230",​ 
 +                        "​price":​ 559.99 
 +                    }, 
 +                    { 
 +                        "​name":​ "​Findologic T-Shirt (Black - White Logo)",​ 
 +                        "​image":​ "​https://​store.com/​some/​path/​to/​image_800x800.jpg",​ 
 +                        "​url":​ "​https://​store.com/​Findologic-T-Shirt/​7c18c5ff8aa548e1bdba6b738ac42f71",​ 
 +                        "​price":​ 499.99 
 +                    } 
 +                ] 
 +            ]]> 
 +        </​value>​ 
 +    </​property>​ 
 +</​allProperties>​ 
 +<!-- ... --> 
 +``` 
 + 
 +## Add product or variant associations 
 + 
 +Sometimes the plugin doesn'​t add the associations needed in the extension. This may cause the associated field to always return `null. It's also the case if third party plugins extend the product entity with an additional table. Other examples include image URLs or color codes from variant properties. 
 + 
 +Decorate `FINDOLOGIC\FinSearch\Export\Search\ProductCriteriaBuilder`:​ 
 + 
 +`src/​Export/​Search/​ProductCriteriaBuilder.php` 
 +```php 
 +<?php 
 + 
 +declare(strict_types=1);​ 
 + 
 +namespace FINDOLOGIC\ExtendFinSearch\Export\Search;​ 
 + 
 +use FINDOLOGIC\FinSearch\Export\Search\ProductCriteriaBuilder as OriginalProductCriteriaBuilder;​ 
 + 
 +class ProductCriteriaBuilder extends OriginalProductCriteriaBuilder 
 +
 +    public function withProductAssociations():​ OriginalProductCriteriaBuilder 
 +    { 
 +        parent::​withProductAssociations();​ 
 + 
 +        $this->​criteria->​addAssociations([ 
 +            // Additional associations 
 +        ]); 
 + 
 +        return $this; 
 +    } 
 + 
 +    public function withVariantAssociations():​ OriginalProductCriteriaBuilder 
 +    { 
 +        parent::​withVariantAssociations();​ 
 + 
 +        $this->​criteria->​addAssociations([ 
 +            '​cover',​ 
 +            '​media'​ 
 +        ]); 
 + 
 +        return $this; 
 +    } 
 +
 +``` 
 + 
 +`src/​Resources/​config/​services.xml` 
 +```xml 
 +<?xml version="​1.0"​ ?> 
 +<​container xmlns:​xsi="​http://​www.w3.org/​2001/​XMLSchema-instance"​ 
 +    xmlns="​http://​symfony.com/​schema/​dic/​services"​ 
 +    xsi:​schemaLocation="​http://​symfony.com/​schema/​dic/​services http://​symfony.com/​schema/​dic/​services/​services-1.0.xsd">​ 
 + 
 +    <​services>​ 
 + 
 +        <!-- ... --> 
 + 
 +        <​service 
 +            id="​FINDOLOGIC\ExtendFinSearch\Export\Search\ProductCriteriaBuilder"​ 
 +            decorates="​FINDOLOGIC\FinSearch\Export\Search\ProductCriteriaBuilder"​ 
 +            public="​true"​ 
 +            decoration-on-invalid="​ignore"​ 
 +            autowire="​true"​ 
 +        /> 
 +         
 +        <!-- ... --> 
 + 
 +    </​services>​ 
 +</​container>​ 
 +``` 
 + 
 +## Cookie Consent for Direct Integration 
 + 
 +In case it's required by the integration,​ you are required to add a cookie consent. Please see the Shopware 6 documentation about [Adding a cookie to the cookie manager]( https://​developer.shopware.com/​docs/​guides/​plugins/​plugins/​storefront/​add-cookie-to-manager). 
 + 
 +There is also an [example](https://​github.com/​findologic/​plugin-shopware-6-extension/​commit/​cf1a0b68a8f590f8eaf818dc038b5903edfbfd80) available in our GitHub repository. 
 + 
 +## Add custom sorting options for API Integration 
 + 
 +The Findologic base plugin already provides sorting options for the most common use-cases. The plugin uses `SortingHandler` to send the currently selected sorting option via API parameters to the Findologic Search-API (see [all available SortingHandlers](https://​github.com/​findologic/​plugin-shopware-6/​tree/​develop/​src/​Core/​Content/​Product/​SalesChannel/​Listing/​SortingHandler)). 
 + 
 +Therefore to handle custom sorting options, create a custom `SortingHandler` in the extension plugin, and override the responsible `SortingHandlerService` to include the created `SortingHandler`. 
 + 
 +### Prerequisites 
 + 
 +Before a custom sorting can be used, make sure to export the value for the custom sort in the `<​sort>​` field in the export. See the [XML Format documentation](:​xml_export_documentation:​xml_format#​sorts) for further details. 
 + 
 +### Implementation 
 + 
 +**Step 1: Create a `SortingHandler`** 
 + 
 +Create the folder structure `Core/​Content/​Product/​SalesChannel/​Listing/​SortingHandler` in the extension plugin, and add a custom sorting handler class. In this example it will be `ThirdPartySortingHandler`:​ 
 + 
 +```php 
 +use FINDOLOGIC\Api\Requests\SearchNavigation\SearchNavigationRequest;​ 
 +use FINDOLOGIC\FinSearch\Core\Content\Product\SalesChannel\Listing\SortingHandler\SortingHandlerInterface;​ 
 +use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;​ 
 + 
 +class ThirdPartySortingHandler implements SortingHandlerInterface 
 +
 +    public function supportsSorting(FieldSorting $fieldSorting):​ bool 
 +    { 
 +        // Enter your custom sort here. 
 +        // To get the name of the sort, select the sort in the storefront and 
 +        // add a dd($fieldSorting) here and refresh the page. 
 +        return $fieldSorting->​getField() === '​product.third_party_extension.field';​ 
 +    } 
 + 
 +    public function generateSorting(FieldSorting $fieldSorting,​ SearchNavigationRequest $searchNavigationRequest):​ void 
 +    { 
 +        $searchNavigationRequest->​setOrder('​shopsort ' . $fieldSorting->​getDirection());​ 
 +    } 
 +
 +``` 
 + 
 +**Step 2: Decorate the `SortingHandlerService`** 
 + 
 +The `SortingHandlerService` holds all available sorting options. Therefore, you want to add your custom sort to this service. The service can be found in `Findologic/​Request/​Handler`. Simply override the `getSortingHandlers` method to include your own custom sorting handler. 
 + 
 +```php 
 +namespace FINDOLOGIC\ExtendFinSearch\Findologic\Request\Handler;​ 
 + 
 +use FINDOLOGIC\ExtendFinSearch\Core\Content\Product\SalesChannel\Listing\SortingHandler\ThirdPartySortingHandler;​ 
 +use FINDOLOGIC\FinSearch\Core\Content\Product\SalesChannel\Listing\SortingHandler\SortingHandlerInterface;​ 
 +use FINDOLOGIC\FinSearch\Findologic\Request\Handler\SortingHandlerService as OriginalSortingHandlerService;​ 
 + 
 +class SortingHandlerService extends OriginalSortingHandlerService 
 +
 +    /** 
 +     * @return SortingHandlerInterface[] 
 +     */ 
 +    protected function getSortingHandlers():​ array 
 +    { 
 +        return array_merge( 
 +            parent::​getSortingHandlers(),​ 
 +            [ 
 +                new ThirdPartySortingHandler() 
 +            ] 
 +        ); 
 +    } 
 +
 +``` 
 + 
 +**Step 3: Add the decorated service to the services.xml** 
 + 
 +As a last step, simply decorate in your `src/​Resources/​config/​services.xml` the service of the main plugin: 
 + 
 +```xml 
 +<?xml version="​1.0"​ ?> 
 +<​container xmlns:​xsi="​http://​www.w3.org/​2001/​XMLSchema-instance"​ 
 +    xmlns="​http://​symfony.com/​schema/​dic/​services"​ 
 +    xsi:​schemaLocation="​http://​symfony.com/​schema/​dic/​services http://​symfony.com/​schema/​dic/​services/​services-1.0.xsd">​ 
 + 
 +    <​services>​ 
 + 
 +        <​service 
 +            id="​FINDOLOGIC\ExtendFinSearch\Findologic\Request\Handler\SortingHandlerService"​ 
 +            decorates="​FINDOLOGIC\FinSearch\Findologic\Request\Handler\SortingHandlerService"​ 
 +            decoration-on-invalid="​ignore"​ 
 +        /> 
 + 
 +    </​services>​ 
 +</​container>​ 
 +``` 
 + 
 +Once this step is done, selecting your relevant sorting option will send the order parameter to the Findologic API.
  
 </​markdown>​ </​markdown>​