Skip to content

SEO URLs for App Routes

SEO URLs for App Routes ​

Overview ​

Storefront pages rendered by app scripts live under /storefront/script/{hook}. That path is technical and cannot carry an entity id. With <seo-url> and <entity-seo-url> elements in the <storefront> section of your manifest.xml, Shopware gives those pages SEO URLs, the same way it does for products and categories.

INFO

This feature was introduced in Shopware 6.7.16.0 and is not available in earlier versions.

There are two kinds of SEO URLs:

  • A static SEO URL (<seo-url>) maps a fixed path such as /imprint to one of your storefront scripts.
  • An entity-bound SEO URL (<entity-seo-url>) generates one URL per entity from a Twig template, for example /blog/{{ ceBlog.translated.title }}, and passes the entity id to your script.

Prerequisites ​

You need an app with at least one storefront script. Read the App Base Guide and the Custom Endpoints guide first. Both kinds of SEO URLs point at scripts stored in Resources/scripts/storefront-<hook>/.

Static SEO URLs ​

Declare a <seo-url> with one or more <path> elements. The name identifies the route inside your app and is also the default script hook.

xml
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/shopware/shopware/trunk/src/Core/Framework/App/Manifest/Schema/manifest-3.0.xsd">
    <meta>
        <name>SwagCompanyPages</name>
        ...
    </meta>
    <storefront>
        <seo-url name="imprint">
            <path>imprint</path>
            <path lang="de-DE">impressum</path>
        </seo-url>
    </storefront>
</manifest>

With this manifest, /imprint and /impressum run the scripts in Resources/scripts/storefront-imprint/. Shopware writes one SEO URL per storefront sales channel domain. It picks the <path> of the domain language and falls back to the shop's default language. Query parameters of the request stay available as hook.query.

Set the hook attribute when the script folder should differ from the route name:

xml
<seo-url name="imprint" hook="company-imprint">
    <path>imprint</path>
</seo-url>

Entity-bound SEO URLs ​

Declare an <entity-seo-url> with an entity attribute and a <default-template>. The entity can be one of your custom entities or a core entity such as product.

xml
<storefront>
    <entity-seo-url name="blog-detail" entity="ce_blog">
        <default-template>blog/{{ ceBlog.translated.title }}</default-template>
    </entity-seo-url>
</storefront>

Shopware generates one SEO URL per entity and language from the template and keeps it up to date whenever the entity is written. Like the SEO URLs of products and categories, this follows the indexing behaviour of the write (for example the indexing-behavior header of the Sync API, or indexing-skip: app_seo_url.seo-url to skip it), and bin/console dal:refresh:index rebuilds them. The template context exposes the entity under its camel-cased name, so ce_blog becomes ceBlog and product becomes product. Every field of the entity is available, translated fields through translated.

The generated URL resolves to /storefront/script/blog-detail?id=<entity-id>, so your script receives the id as hook.query.id:

twig
// Resources/scripts/storefront-blog-detail/script.twig
{% set post = services.store.search('ce_blog', { 'ids': [hook.query.id] }).first %}

{% do hook.page.addExtension('post', post) %}

{% do hook.setResponse(
    services.response.render('@SwagBlog/storefront/page/blog/detail.html.twig', { 'page': hook.page })
) %}

Permissions ​

Your app needs <read> permission for the entity and for every association your <default-template> uses. A template that reads product.manufacturer.name needs both entities:

xml
<storefront>
    <entity-seo-url name="product-detail" entity="product">
        <default-template>{{ product.manufacturer.name }}/{{ product.translated.name }}</default-template>
    </entity-seo-url>
</storefront>
<permissions>
    <read>product</read>
    <read>product_manufacturer</read>
</permissions>

Your own custom entities need no extra permission: your app has full access to them anyway. The generated paths are public, so only use fields in the template that may appear in a URL.

Merchant configuration ​

Entity-bound routes appear in the Administration under Settings > SEO as storefront.app.<app name>.<name>, for example storefront.app.SwagBlog.blog-detail. Merchants can adjust the template per sales channel and override single URLs like they do for products. Changing a template regenerates the URLs automatically. Your <default-template> is only the initial value: app updates don't overwrite a template that already exists. Changing the entity of a route resets every template of that route, including sales channel overrides. Declare a new name if a route needs a fresh template for the same entity.

Linking to your pages ​

Use the seoUrl Twig function with the technical route and parameters in your storefront templates. The placeholder is replaced with the SEO URL when the page is rendered:

twig
<a href="{{ seoUrl('frontend.script_endpoint', { 'hook': 'blog-detail', 'id': post.id }) }}">
    {{ post.translated.title }}
</a>

<a href="{{ seoUrl('frontend.script_endpoint', { 'hook': 'imprint' }) }}">
    {{ 'swag-company-pages.imprint'|trans }}
</a>

Lifecycle ​

  • Install and update: Shopware stores the declared routes and creates the default SEO URL template for entity-bound routes that don't have one yet. Routes that an update no longer declares lose their SEO URLs and templates.
  • Activation and update: The static SEO URLs are written right away. The entity-bound URLs are generated through the message queue, so make sure a worker processes messages.
  • Hook changes: Changing the hook of a route keeps its SEO URLs, including the merchant's edits, and points them at the new script.
  • Deactivation and uninstall: The app's SEO URLs are marked as deleted and stop resolving. Uninstalling also removes the SEO URL templates, unless the merchant keeps the app data.
  • New sales channel domains: Like product URLs, the static SEO URLs for a new domain show up on the next activation or update of the app, or after bin/console dal:refresh:index.

Validation rules ​

The manifest schema checks the structure of both elements:

  • name and hook must match [a-z0-9]+(-[a-z0-9]+)*, and name must be unique across all <seo-url> and <entity-seo-url> elements of the app.
  • A <seo-url> needs at least one non-empty <path>.
  • An <entity-seo-url> needs an entity attribute and a non-empty <default-template>.

On app install and update, Shopware also checks:

  • Static paths must not contain characters that aren't allowed in URLs and must not yet be used by a storefront route such as /account, by another app or by an existing SEO URL.
  • A script hook belongs to the first app that declares it in an SEO URL. Within your app, a hook can be used by at most one <seo-url> and at most one <entity-seo-url>.
  • The entity of an <entity-seo-url> must exist and must not be a mapping or translation entity, and your app needs the permissions described above.

Limitations ​

  • SEO URLs are generated for storefront sales channels only. Headless sales channels are not supported yet.
  • No hreflang links are generated for app routes.
Was this page helpful?
UnsatisfiedSatisfied
Be the first to vote!
0.0 / 5  (0 votes)