Search code examples
shopwareshopware6

Find SEO URL of variant on product detail page


I am trying to lookup the seoUrl of variants displayed on a product detail page - in the configurator.html.twig file. I have the option Id and have tried passing it as productId for the seoUrl function - but it doesn't return the correct seoUrl.

Searching for a solution, I found this question: Show all variations on the product detail page in Shopware 6 - which also lacks an answer. But it hinted that you should add the data using a Subscriber - is that really necessary?


Solution

  • You may decorate the ProductDetailRoute, fetch the parent and with its children association and iterate them in the storefront template.

    <service id="MyPlugin\Core\Content\Product\SalesChannel\Detail\ProductDetailRouteDecorator" public="true" decorates="Shopware\Core\Content\Product\SalesChannel\Detail\ProductDetailRoute">
        <argument type="service" id="MyPlugin\Core\Content\Product\SalesChannel\Detail\ProductDetailRouteDecorator.inner"/>
        <argument type="service" id="sales_channel.product.repository"/>
    </service>
    
    class ProductDetailRouteDecorator extends AbstractProductDetailRoute
    {
        private SalesChannelRepositoryInterface $productRepository;
    
        private AbstractProductDetailRoute $decorated;
    
        public function __construct(
            AbstractProductDetailRoute $decorated,
            SalesChannelRepositoryInterface $productRepository
        ) {
            $this->decorated = $decorated;
            $this->productRepository = $productRepository;
        }
    
        public function getDecorated(): AbstractProductDetailRoute
        {
            return $this->decorated;
        }
    
        public function load(string $productId, Request $request, SalesChannelContext $context, Criteria $criteria): ProductDetailRouteResponse
        {
            $response = $this->getDecorated()->load($productId, $request, $context, $criteria);
            $product = $response->getProduct();
    
            if (!$product->getParentId()) {
                return $response;
            }
    
            $criteria = new Criteria([$product->getParentId()]);
            $criteria->addAssociation('children');
    
            $parent = $this->productRepository->search($criteria, $context)->first();
            $product->setParent($parent);
    
            return new ProductDetailRouteResponse($product, $response->getConfigurator());
        }
    }
    
    {% if page.product.parent.children is defined %}
        {% for child in page.product.parent.children %}
            {{ seoUrl('frontend.detail.page', { productId: child.id }) }}
        {% endfor %}
        <br>
    {% endif %}
    

    Sample output:

    http://localhost/Intelligent-Marble-Ultra-Beef/0491895660e94e32938022263595f861
    http://localhost/Intelligent-Marble-Ultra-Beef/7c9f91d9051e40e0ba13d0e885e98d83
    http://localhost/Intelligent-Marble-Ultra-Beef/f25c641abee446df82e1227cf200186c
    

    Variation on this solution

    A little more complex but with a lesser performance impact:

    $criteria = new Criteria();
    $criteria->addFilter(new EqualsFilter('parentId', $product->getParentId()));
    
    $ids = $this->productRepository->searchIds($criteria, $context)->getIds();
    $product->addExtension('childrenIds', new ArrayStruct($ids));
    
    {% if page.product.extensions.childrenIds is defined %}
        {% for childId in page.product.extensions.childrenIds.all() %}
            {{ seoUrl('frontend.detail.page', { productId: childId }) }}
            <br>
        {% endfor %}
    {% endif %}