For developers
Entity
Landing pages are the DAL entity repertus_seo_filter_landingpage, reachable via the Admin API under /api/repertus-seo-filter-landingpage, including search (/api/search/repertus-seo-filter-landingpage) and the Sync API.
Important fields and associations:
| Field | Meaning |
|---|---|
name, title, description, metaTitle, metaDescription, keywords | Translatable text fields; title and metaTitle are required |
breadcrumb | Text of the breadcrumb entry; no admin field, API only |
ogTitle, ogDescription, openGraphMediaId | Open Graph data; stored, currently not output in the storefront |
enabled, robotType, sitemapPrio, changeFreq, sitemapExclude | State and SEO strategy |
configurationMode | shared or per_sales_channel |
categoryId, useScNav | Category or sales channel navigation |
properties, manufacturers | Filter combination (M:N to property_group_option and product_manufacturer) |
parameters | Additional listing parameters as type/value pairs, for example min-price = 10. Only maintainable via API, no admin UI. |
salesChannels | Visibility in shared mode |
children, parentId, salesChannelId | Sales channel variants in per_sales_channel mode |
cmsPageId, slotConfig | Layout and element contents |
canonicalForeignKey | Target of the canonical URL (foreign key of a seo_url) |
Creating a landing page via API:
POST /api/repertus-seo-filter-landingpage
{
"name": "Tableware in cream",
"title": "Tableware/Cream",
"metaTitle": "Buy cream tableware",
"enabled": true,
"configurationMode": "shared",
"robotType": "follow, index",
"categoryId": "0190…",
"properties": [{ "id": "0190…" }],
"salesChannels": [{ "id": "0190…" }]
}After creation the indexer generates the SEO URLs; asynchronously with queue indexing.
Switching the configuration mode via API
The mode switch is not a field update but two actions that create or merge variants and move the SEO URLs along:
POST /api/_action/repertus/seo-filter-landingpage/{id}/convert-to-per-sales-channel
POST /api/_action/repertus/seo-filter-landingpage/{id}/convert-to-shared
{ "chosenVariantId": "…" }Admin API writes to landing pages in per_sales_channel mode are rejected with HTTP 403 without an active in-app extension; converting back to shared is always allowed.
Storefront routes
| Route | Purpose |
|---|---|
GET /landingpage/{landingpageId} (frontend.repertus.landingpage.page) | Storefront page; the SEO URL points here |
GET /widgets/landingpage/{navigationId}/check | Check from the listing whether the active filters match a landing page |
/widgets/cms/tonur-seo-filter-landingpage/{landingpageId} and …/filter | XHR loading of listing and filters |
Events
Tonur\SeoFilterLandingpages\Storefront\Page\Landingpage\LandingpagePageLoadedEventfires after the storefront page is loaded and carriesLandingpagePage, context and request. The page extends the navigation page bylandingpageandparentCategoryUrl.Tonur\SeoFilterLandingpages\Core\Content\Landingpage\Event\SeoFilterLandingpageIndexerEventfires after indexing and triggers SEO URL generation.
Templates
The plugin extends these storefront templates:
storefront/page/content/index.html.twig: breadcrumb blockstorefront/layout/breadcrumb.html.twig: landing page as breadcrumb elementstorefront/component/product/listing.html.twig: listing options (landing page filters, back button, check URL)storefront/element/cms-element-product-listing.html.twig: XHR URLs of the listing
The JavaScript plugin TonurSeoFilterLandingpageListingPlugin overrides Shopware's Listing plugin globally (PluginManager.override('Listing', …)). It locks the landing page filters, renders the back button and sends the redirect check to frontend.repertus.landingpage.check on every filter change in every listing. Custom listing overrides have to extend it or trigger the check themselves.
Generating landing pages
The command bin/console repertus:seo-filter-landingpage:generate-for-filter creates landing pages in bulk via the Sync API. The data is provided by services extending Tonur\SeoFilterLandingpages\DataProvider\LandingpageDataProvider and tagged with tonur.seo_filter_landingpages.landingpage_data_provider:
class ColorLandingpageProvider extends LandingpageDataProvider
{
public function getAction(): string { return 'upsert'; }
public function getEntity(): string { return 'repertus_seo_filter_landingpage'; }
public function getPayload(): array { /* list of landing page payloads */ }
}After the import the SEO URLs of the new landing pages are generated right away.
SEO URL template
The route frontend.repertus.landingpage.page is registered with the SEO URL system. Inside the template all fields of the landing page are available under landingpage, complemented by propertyIds, manufacturerIds, parameters and, with an assigned category, seoBreadcrumb.