integration_documentation:plugin:en:integration:shopware_6:extension

integration_documentation:plugin:en:integration:shopware_6:extension

This is an old revision of the document!


Shopware 6 plugin extension

The Findologic extension plugin for Shopware 6 allows manual adaptions to the behavior of the main Findologic plugin.

It's possible override, extend and replace components, to fit the needs of your store. The most common use case is to add additional data to the Findologic product export.

Download the latest zip file from our GitHub release page.

Follow the installation instructions in the Shopware documentation.

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.

Decorators

Adaptions need to be done using Symfony decorators.

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.

src/Resources/config/services.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\Adapters\AttributeAdapter"
            decorates="FINDOLOGIC\FinSearch\Export\Adapters\AttributeAdapter"
            public="true"
            decoration-on-invalid="ignore"
        >
            <argument type="service" id="service_container" />
            <argument type="service" id="FINDOLOGIC\FinSearch\Struct\Config" />
            <argument 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
            id="FINDOLOGIC\ExtendFinSearch\Export\Adapters\DefaultPropertiesAdapter"
            decorates="FINDOLOGIC\FinSearch\Export\Adapters\DefaultPropertiesAdapter"
            public="true"
            decoration-on-invalid="ignore"
        >
            <argument type="service" id="FINDOLOGIC\FinSearch\Struct\Config" />
            <argument type="service" id="fin_search.sales_channel_context" />
            <argument type="service" id="Shopware\Core\Framework\Adapter\Translation\Translator" />
        </service>
 
    </services>
</container>

AttributeAdapter

This class can be used to customize the export of all attributes.

src/Export/Adapters/AttributeAdapter.php

<?php
 
declare(strict_types=1);
 
namespace FINDOLOGIC\ExtendFinSearch\Export\Adapters;
 
use FINDOLOGIC\Export\Data\Attribute;
use FINDOLOGIC\FinSearch\Export\Adapters\AttributeAdapter as OriginalAttributeAdapter;
use Shopware\Core\Content\Product\ProductEntity;
 
class AttributeAdapter extends OriginalAttributeAdapter
{
    public function adapt(ProductEntity $product): array
    {
        $attributes = parent::adapt($product);
 
//        $attributes[] = new Attribute(
//            'Some attribute name',
//            ['I am an attribute value!']
//        );
 
        return $attributes;
    }
}

DefaultPropertiesAdapter

This class can be used to customize the export of the default properties.

src/Export/Adapters/AttributeAdapter.php

<?php
 
declare(strict_types=1);
 
namespace FINDOLOGIC\ExtendFinSearch\Export\Adapters;
 
use FINDOLOGIC\Export\Data\Property;
use FINDOLOGIC\FinSearch\Export\Adapters\DefaultPropertiesAdapter as OriginalDefaultPropertiesAdapter;
use Shopware\Core\Content\Product\ProductEntity;
 
class DefaultPropertiesAdapter extends OriginalDefaultPropertiesAdapter
{
    public function adapt(ProductEntity $product): array
    {
        $properties = parent::adapt($product);
 
//        $properties[] = new Property(
//            'Some property name',
//            ['' => 'I am a property value!']
//        );
 
        return $properties;
    }
}

Autoloading

You only need composer autoloading in case you require additional composer dependencies.

https://docs.findologic.com/lib/exe/fetch.php?t=1603273975&tok=ddd8c2&media=integration_documentation:extension.png

While composer autoloading is disabled by default, you can always enable it by uncommenting the marked line in \FINDOLOGIC\ExtendFinSearch\ExtendFinSearch.

Examples

The base DefaultPropertiesAdapter allows you to add properties by extending the adapt function.

//...
public function adapt(ProductEntity $product): array
{
    $properties = parent::adapt($product);
 
    $properties[] = new Property(
        'Some property name',
        ['' => 'I am a property value!']
    );
 
    return $properties;
}

The part ['' => 'I am a property value!'] has an empty string as array index. An empty string as an array key, simply means that there is no usergroup, as property data can be usergroup-specific.

You can read more about usergroups in the libflexport documentation, which is the library used to build the export xml.

In case you have usergroup-specific data, properties which should be available for all usergroups, must be added individually for each usergroup.

By default Findologic maps all variants into a single product during product export. However there is still the possibility to show variant information on listing pages, but this information needs to be exported to Findologic.

Please follow the next sections to understand how the core plugin maps variants into a single product.

Basic product mapping

Shopware works with display groups, which are used to take one product and all its variants, and map them to one product. See the following table, which represents a simplified product table:

id parentId name displayGroup
1 null Findologic T-Shirt null
2 1 Findologic T-Shirt (Black - White Logo) 1
3 1 Findologic T-Shirt (Gray - Orange Logo) 1

These are three separate products, two of those products are variants of Findologic T-Shirt. The ProductSearcher is responsible for fetching all variants of one display group, and mapping them together. This results in main product no longer being Findologic T-Shirt, but instead Findologic T-Shirt (Black - White Logo). To still have the data of the main product available in the export, the main product, and all other variants, are assigned as children of this variant.

Structure:

Findologic T-Shirt (Black - White Logo)
├── children
│   ├── Findologic T-Shirt (Gray - Orange Logo)
│   └── Findologic T-Shirt

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 is configured, the product has its own display group, so none of this reordering is happening.

Basic code structure

This extension will use the defined events from the main plugin.

src/Subscriber/ProductSubscriber.php

<?php
 
declare(strict_types=1);
 
namespace FINDOLOGIC\ExtendFinSearch\Subscriber;
 
use FINDOLOGIC\Export\Data\Property;
use FINDOLOGIC\FinSearch\Export\Adapters\AdapterFactory;
use FINDOLOGIC\FinSearch\Export\Events\AfterItemBuildEvent;
use FINDOLOGIC\FinSearch\Export\Events\AfterVariantAdaptEvent;
use FINDOLOGIC\FinSearch\Export\Events\BeforeItemAdaptEvent;
use Shopware\Core\Content\Product\ProductEntity;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
class ProductSubscriber implements EventSubscriberInterface
{
    /** @var AdapterFactory */
    private $adapterFactory;
 
    private $variantData = [];
 
    public function __construct(AdapterFactory $adapterFactory)
    {
        $this->adapterFactory = $adapterFactory;
    }
 
    public static function getSubscribedEvents()
    {
        return [
            BeforeItemAdaptEvent::NAME => 'beforeItemAdapt',
            AfterVariantAdaptEvent::NAME => 'afterVariantAdapted',
            AfterItemBuildEvent::NAME => 'afterItemCompleted'
        ];
    }
 
    public function beforeItemAdapt()
    {
        $this->variantData = [];
    }
 
    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)
                ])
            );
        }
    }
}

src/Resources/config/services.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\FinSearch\Export\Adapters\AdapterFactory" />
        </service>
 
    </services>
</container>

Add basic variant data

src/Subscriber/ProductSubscriber.php

//...
 
public function afterVariantAdapted(AfterVariantAdaptEvent $event)
{
    $product = $event->getProduct();
 
    $this->variantData[] = [
        'name' => $this->getName($product),
        'image' => $this->getImageUrl($product),
        'price' => $this->getPrice($product)
    ];
}
 
//...
 
private function getName(ProductEntity $productEntity): string
{
    $name = $this->adapterFactory->getNameAdapter()->adapt($productEntity);
 
    return $name ? $name->getValues()[''] : '';
}
 
private function getImageUrl(ProductEntity $productEntity): string
{
    $images = $this->adapterFactory->getUrlAdapter()->adapt($productEntity);
 
    return $images ? $images->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()[''] : '';
}
 
//...

Add images

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
 
declare(strict_types=1);
 
namespace FINDOLOGIC\ExtendFinSearch\Export\Search;
 
use FINDOLOGIC\FinSearch\Export\Search\ProductCriteriaBuilder as OriginalProductCriteriaBuilder;
use FINDOLOGIC\FinSearch\Utils\Utils;
 
class ProductCriteriaBuilder extends OriginalProductCriteriaBuilder
{
    public function withProductAssociations(): OriginalProductCriteriaBuilder
    {
        Utils::addProductAssociations($this->criteria);
 
        $this->criteria->addAssociations([
            // Additional associations
        ]);
 
        return $this;
    }
 
    public function withVariantAssociations(): OriginalProductCriteriaBuilder
    {
        Utils::addVariantAssociations($this->criteria);
 
        $this->criteria->addAssociations([
            'cover',
            'media
        ]);
 
        return $this;
    }
}

src/Resources/config/services.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"
        >
            <argument type="service" id="fin_search.sales_channel_context" />
            <argument type="service" id="Shopware\Core\System\SystemConfig\SystemConfigService" />
        </service>
 
        <!-- ... -->
 
    </services>
</container>