Links
Features
Inspections
Reports decorated services that extend or inject the concrete implementation where Shopware exposes an abstract decoration contract.
Before:
final class CustomProductRoute extends ProductRoute
{
public function load(string $productId, Request $request, SalesChannelContext $context): ProductRouteResponse
{
return parent::load($productId, $request, $context);
}
}
After:
final class CustomProductRoute extends AbstractProductRoute
{
public function __construct(private readonly AbstractProductRoute $decorated)
{
}
public function getDecorated(): AbstractProductRoute
{
return $this->decorated;
}
public function load(string $productId, Request $request, SalesChannelContext $context): ProductRouteResponse
{
return $this->decorated->load($productId, $request, $context);
}
}
Reports direct creation of the default Shopware context when request or execution context should be passed explicitly.
Before:
public function load(Criteria $criteria): EntitySearchResult
{
$context = Context::createDefaultContext();
return $this->productRepository->search($criteria, $context);
}
After:
public function load(Criteria $criteria, SalesChannelContext $context): EntitySearchResult
{
return $this->productRepository->search($criteria, $context->getContext());
}
Reports parent methods marked with PHPDoc @abstract when the child class does not implement them yet.
Before:
class MyRoute extends AbstractProductRoute
{
// Parent method is still concrete but already marked @abstract.
}
After:
class MyRoute extends AbstractProductRoute
{
public function getDecorated(): AbstractProductRoute
{
throw new DecorationPatternException(self::class);
}
}
Reports direct id filters when the id can be passed through the id-aware Criteria API instead.
Before:
public function load(string $productId, Context $context): EntitySearchResult
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('id', $productId));
return $this->productRepository->search($criteria, $context);
}
After:
public function load(string $productId, Context $context): EntitySearchResult
{
$criteria = new Criteria([$productId]);
return $this->productRepository->search($criteria, $context);
}
Reports FOREIGN_KEY_CHECKS changes inside Shopware migration or plugin update() methods. It does not report unrelated SQL strings outside update code.
Before:
final class Migration1710000000Example extends MigrationStep
{
public function update(Connection $connection): void
{
$connection->executeStatement('SET FOREIGN_KEY_CHECKS=0');
$connection->executeStatement('DELETE FROM product WHERE id = :id', ['id' => $id]);
$connection->executeStatement('SET FOREIGN_KEY_CHECKS=1');
}
}
After:
final class Migration1710000000Example extends MigrationStep
{
public function update(Connection $connection): void
{
$connection->delete('product_translation', ['product_id' => $id]);
$connection->delete('product', ['id' => $id]);
}
}
Reports direct reads of the Administration user store token so integrations use explicit token handling instead of user-entity internals.
Before:
public function connect(UserEntity $user): void
{
$token = $user->getStoreToken();
$this->client->connect($token);
}
After:
public function connect(string $token): void
{
$this->client->connect($token);
}
Reports DAL repository searches inside loops so repeated queries can be replaced with a batched lookup.
Before:
public function loadProducts(array $productIds, Context $context): array
{
$products = [];
foreach ($productIds as $productId) {
$criteria = new Criteria([$productId]);
$products[] = $this->productRepository->search($criteria, $context)->first();
}
return $products;
}
After:
public function loadProducts(array $productIds, Context $context): EntitySearchResult
{
$criteria = new Criteria($productIds);
return $this->productRepository->search($criteria, $context);
}
Reports EqualsFilter and EqualsAnyFilter calls that filter directly on the entity id field.
Before:
public function load(array $ids, Context $context): EntitySearchResult
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsAnyFilter('id', $ids));
return $this->productRepository->search($criteria, $context);
}
After:
public function load(array $ids, Context $context): EntitySearchResult
{
$criteria = new Criteria();
$criteria->setIds($ids);
return $this->productRepository->search($criteria, $context);
}
Forward the sales channel id to system config reads
ShopwareForwardSalesChannelContextToSystemConfigInspection
ShopwareForwardSalesChannelContextToSystemConfigInspection Reports system config reads in a sales-channel scope that do not pass the active sales-channel id.
Before:
public function load(SalesChannelContext $salesChannelContext): string
{
return $this->systemConfigService->get('Acme.config.label');
}
After:
public function load(SalesChannelContext $salesChannelContext): string
{
return $this->systemConfigService->get(
'Acme.config.label',
$salesChannelContext->getSalesChannelId()
);
}
Reports literal scheduled task intervals below the Shopware minimum.
Before:
public static function getDefaultInterval(): int
{
return 60;
}
After:
public static function getDefaultInterval(): int
{
return 300;
}
Avoid session access in Store API and payment handlers
ShopwareNoSessionInPaymentHandlerAndStoreApiInspection
ShopwareNoSessionInPaymentHandlerAndStoreApiInspection Reports Symfony session reads in Shopware payment handlers and Store API routes. These entrypoints should use their explicit method inputs or persisted state instead of depending on storefront session data.
Reported session access:
public function pay(
Request $request,
PaymentTransactionStruct $transaction,
Context $context,
?Struct $validateStruct
): ?RedirectResponse {
$token = $this->session->get('checkout-token');
return null;
}
Use the route or handler inputs:
public function switchContext(RequestDataBag $data, SalesChannelContext $context): ContextTokenResponse
{
$paymentMethodId = $data->get('paymentMethodId');
$contextToken = $context->getToken();
// Update state through the sales-channel context services.
}
Payment handlers receive transaction data explicitly:
public function pay(
Request $request,
PaymentTransactionStruct $transaction,
Context $context,
?Struct $validateStruct
): ?RedirectResponse {
$transactionId = $transaction->getOrderTransactionId();
// Load payment state from the transaction/order data, not from the session.
}
Reports DAL fields that cannot be hydrated cleanly because the linked entity is missing matching access.
Definition field:
protected function defineFields(): FieldCollection
{
return new FieldCollection([
new StringField('technical_name', 'technicalName'),
]);
}
Entity access:
class ExampleEntity extends Entity
{
protected ?string $technicalName = null;
public function getTechnicalName(): ?string
{
return $this->technicalName;
}
}
Reports exact Twig activeRoute comparisons when the referenced route name cannot be found.
Checked comparison:
{% if activeRoute == 'frontend.account.home.page' %}
active
{% endif %}
Prefix checks stay valid:
{% if activeRoute starts with 'frontend.account.' %}
active
{% endif %}
Reports literal sw_icon calls when the icon pack or SVG file cannot be resolved.
Checked icon call:
{% sw_icon 'shopping-bag' style { 'pack': 'solid' } %}
Reports Twig block overrides that target blocks already marked for removal in indexed Shopware templates.
Override checked against the indexed block metadata:
{% block page_product_detail_buy_form %}
{{ parent() }}
{% endblock %}
Reports Administration translation keys that are used in code or templates but missing from nearby snippet JSON files.
Usage:
this.$tc('swag-example.card.headline');
Snippet file:
{
"swag-example": {
"card": {
"headline": "Example"
}
}
}
Reports component extensions where the static parent component name cannot be resolved.
Checked component extension:
Shopware.Component.extend('swag-example-child', 'sw-product-detail', {
template,
});
Reports missing or incomplete Shopware Store metadata in plugin composer files.
composer.json:
{
"extra": {
"shopware-plugin-class": "Swag\\Example\\SwagExample",
"label": {
"en-GB": "Example extension"
},
"manufacturerLink": {
"en-GB": "https://example.com"
}
}
}
Reports repository reads and aggregations in app scripts when the app manifest does not grant the matching entity read permission, while respecting Shopware's write-permission read dependency.
Repository access that needs read permission:
{% set products = services.repository.search('product', criteria) %}
{% set total = services.repository.aggregate('product', criteria) %}
Manifest permissions:
<permissions>
<read>product</read>
<update>order</update>
</permissions>
Completion
Completion Paid Twig Storefront
Completes Shopware storefront template names and helper arguments for Shopware Twig tags and helpers such as sw_extends, sw_include, sw_icon, and sw_thumbnails.
Storefront Twig:
{% sw_extends '@Storefront/storefront/page/content/index.html.twig' %}
{% sw_include '@Storefront/storefront/component/product/card/box.html.twig' %}
{% sw_icon 'shopping-bag' style { 'pack': 'solid' } %}
{% sw_thumbnails 'product-image-thumbnails' with {
media: product.cover.media
} %}
Completion Paid Twig Storefront
Completes indexed sw_thumbnails names so repeated thumbnail blocks stay connected to existing storefront usages.
Thumbnail block:
{% sw_thumbnails 'product-image-thumbnails' with {
media: product.cover.media
} %}
Completion Paid Twig
Completes route names in Twig activeRoute comparisons so navigation states stay connected to real routes.
Exact route comparison:
{% if activeRoute == 'frontend.account.home.page' %}
active
{% endif %}
Prefix check for route groups:
{% if activeRoute starts with 'frontend.account.' %}
active
{% endif %}
Multiple route names:
{% if activeRoute in [
'frontend.account.profile.page',
'frontend.account.address.page',
'frontend.account.order.page'
] %}
active
{% endif %}
Inline class expression:
<a class="nav-link {{ activeRoute == 'frontend.checkout.cart.page' ? 'is-active' }}">
{{ 'checkout.cartTitle'|trans }}
</a>
Navigation Paid Twig
Navigates from literal route names in Twig activeRoute checks to the matching route definition.
Exact route comparison:
{% if activeRoute == 'frontend.account.home.page' %}
active
{% endif %}
Prefix check for route groups:
{% if activeRoute starts with 'frontend.account.' %}
active
{% endif %}
Multiple route names:
{% if activeRoute in [
'frontend.account.profile.page',
'frontend.account.address.page',
'frontend.account.order.page'
] %}
active
{% endif %}
Inline class expression:
<a class="nav-link {{ activeRoute == 'frontend.checkout.cart.page' ? 'is-active' }}">
{{ 'checkout.cartTitle'|trans }}
</a>
Completion Paid PHP Twig Scss
Completes Shopware feature flag names across PHP, Twig, and SCSS and keeps them linked to the indexed flag definitions.
PHP:
if (Feature::isActive('v6.7.0.0')) {
// new behavior
}
Twig:
{% if feature('v6.7.0.0') %}
New storefront markup
{% endif %}
SCSS:
@if feature('v6.7.0.0') {
.product-box { gap: 1rem; }
}
Navigation Paid PHP Twig Scss
Navigates from Shopware feature flag usages in PHP, Twig, and SCSS to the indexed feature definition.
PHP:
if (Feature::isActive('v6.7.0.0')) {
// new behavior
}
Twig:
{% if feature('v6.7.0.0') %}
New storefront markup
{% endif %}
SCSS:
@if feature('v6.7.0.0') {
.product-box { gap: 1rem; }
}
Completion Paid PHP
Completes plugin and app system config keys in PHP read and write calls based on indexed config XML definitions.
System config calls:
$label = $this->systemConfigService->get('SwagExample.config.buttonLabel');
$this->systemConfigService->set('SwagExample.config.buttonLabel', 'Buy now');
Completion Paid PHP JavaScript
Completes DAL field names, associations, and nested paths in PHP Criteria code and Administration repository code.
PHP Criteria:
$criteria = (new Criteria())
->addAssociation('manufacturer')
->addFilter(new EqualsFilter('manufacturer.name', $name));
Administration repository:
const criteria = new Criteria();
criteria.addAssociation('manufacturer');
criteria.addFilter(Criteria.equals('manufacturer.name', value));
Completion Paid Twig Administration
Completes snippet translation calls inside Administration Twig and HTML attributes.
Administration template attribute:
<sw-text-field :label="$tc('swag-example.form.label')" />
Completion Paid JavaScript Administration
Completes registered Administration mixin names in JavaScript and TypeScript mixin lookups.
Mixin lookup:
const notification = Shopware.Mixin.getByName('notification');
Completion Paid JavaScript Administration
Completes Administration module route names in route-link and router usages.
Module route usage:
this.$router.push({ name: 'sw.product.detail', params: { id } });
Completion Paid Twig Administration
Completes available Administration extension slot names for the component used in Twig or HTML templates, using the same slot model as navigation and Vue validation.
Slot usage:
<sw-page>
<template #smart-bar-actions>
<sw-button variant="primary">Save</sw-button>
</template>
</sw-page>
Completion Paid Twig App script
Completes entity names and config keys in type-resolved app-script facade calls, including repository, store, writer, and system config services.
App script facade calls:
{% set products = services.repository.search('product', criteria) %}
{% set ids = services.store.ids('product', criteria) %}
{% set result = services.writer.upsert('custom_entity_blog', payload) %}
{% set threshold = services.config.app('freeShippingThreshold') %}
{% set value = services.config.get('AgencyWorkflowApp.config.freeShippingThreshold') %}
Completion Paid PHP JavaScript
Completes Shopware snippet keys in PHP and Administration JavaScript translation calls.
PHP:
$this->trans('checkout.cart.title');
Administration JavaScript:
this.$tc('sw-product.detail.title');
Navigation
Navigation Paid Twig Storefront
Navigates from Shopware storefront Twig references, icon names, and thumbnail names to the resolved target, with template usage support for Shopware inheritance tags.
Storefront Twig reference:
{% sw_extends '@Storefront/storefront/page/product-detail/index.html.twig' %}
{% sw_include '@Storefront/storefront/component/product/card/box.html.twig' %}
{% sw_embed '@Storefront/storefront/component/product/card/action.html.twig' %}{% end_sw_embed %}
{% sw_icon 'shopping-bag' style { 'pack': 'solid' } %}
{% sw_thumbnails 'product-image-thumbnails' with {
media: product.cover.media
} %}
Navigation Paid Twig Storefront
Navigates between literal sw_thumbnails names and the indexed Twig usages that define the same thumbnail key.
Thumbnail reference:
{% sw_thumbnails 'product-image-thumbnails' with {
media: product.cover.media
} %}
Navigation Paid JavaScript Administration
Navigates from Administration component names and slot usages to useful targets, including lazy definition files, registry key literals, resolved templates, and slot definitions.
Component registry and definition targets:
Shopware.Component.register('sw-page', () => import('src/app/component/structure/sw-page/index'));
Shopware.Component.override('sw-product-detail', {
template,
});
Slot usage:
<sw-page>
<template #smart-bar-actions>
<sw-button variant="primary">Save</sw-button>
</template>
</sw-page>
Navigation Paid JavaScript Administration
Navigates from Administration route names to the module route definition that registered them.
Module route reference:
this.$router.push({ name: 'sw.product.detail', params: { id } });
Route definition:
Shopware.Module.register('swag-example', {
routes: {
detail: {
component: 'swag-example-detail',
path: 'detail/:id',
},
},
});
Navigation Paid PHP
Navigates from PHP system config read/write usages and Twig config() calls to the matching plugin or app configuration field.
Config key usage:
$label = $this->systemConfigService->get('SwagExample.config.buttonLabel');
$this->systemConfigService->set('SwagExample.config.buttonLabel', 'Buy now');
Twig config usage:
{{ config('SwagExample.config.buttonLabel') }}
Navigation Paid Twig App script
Navigates from type-resolved app-script facade string arguments to platform entities, app custom entities, and system config XML entries.
Entity and config references:
{% set products = services.repository.search('product', criteria) %}
{% set total = services.repository.aggregate('product', criteria) %}
{% set result = services.writer.upsert('custom_entity_blog', payload) %}
{% set threshold = services.config.app('freeShippingThreshold') %}
{% set full = services.config.get('AgencyWorkflowApp.config.freeShippingThreshold') %}
Linemarker
Linemarker Paid Twig Administration
Shows related-target markers between Administration component usages and their definition, registry key, and resolved template targets.
Component registration:
Shopware.Component.register('sw-page', () => import('src/app/component/structure/sw-page/index'));
Template usage:
<swag-example-card />
Linemarker Paid Twig Administration
Shows related-target markers for Administration extension slots, with goto navigation from slot attributes and the surrounding <template> tag.
Slot definition:
<div class="sw-page">
<slot name="content"></slot>
<slot name="smart-bar-actions"></slot>
</div>
Slot usage with linemarker target:
<sw-page>
<template #content>
<sw-card>
Page content
</sw-card>
</template>
<template #smart-bar-actions>
<sw-button variant="primary">Save</sw-button>
</template>
</sw-page>
MCP Tools
Lists indexed Shopware project context as CSV for Administration components, routes, mixins, snippets, icons, feature flags, theme config, and system config.
Tool examples:
{ "type": "admin_component", "search": "sw-page" }
{ "type": "snippet", "search": "checkout" }
{ "type": "system_config", "search": "SwagBasicExample" }
Lists plugins and apps found in the opened shop project from Composer metadata and app manifests, including local, root, and Composer-managed extensions.
Returned CSV columns:
kind,identifier,name,composerName,baseClass,version,managedByComposer,relation,filePath,rootPath,metadata
Tool examples:
{ "search": "custom/plugins|frosh", "limit": 20 }
{ "search": "composer_plugin|shopware-app" }
Other
Other Paid Twig Storefront
Connects Shopware's template inheritance tags with Symfony's expanded Twig template usage API, including embed, use, import, and from tags.
Supported template tags:
{% sw_extends '@Storefront/storefront/base.html.twig' %}
{% sw_include '@Storefront/storefront/component/product/card/box.html.twig' %}
{% sw_embed '@Storefront/storefront/component/box.html.twig' %}{% end_sw_embed %}
{% sw_use '@Storefront/storefront/utilities/blocks.html.twig' %}
{% sw_import '@Storefront/storefront/utilities/macros.html.twig' as ui %}
{% sw_from '@Storefront/storefront/utilities/forms.html.twig' import form_field %}
Other Paid Vue Administration
Exposes resolved Shopware Administration component slots as Vue Poly Symbols so valid extension slots are accepted by Vue slot validation while unknown names still stay visible.
Valid Shopware Administration slots:
<sw-page>
<template #search-bar>
<sw-search-bar />
</template>
<template #smart-bar-actions>
<sw-button variant="primary">Save</sw-button>
</template>
</sw-page>
Other Paid Twig App script
Provides app-script Twig globals and hook context variables for scripts under Resources/scripts, including include files used by macros.
Executable hook script:
{% set products = services.repository.search('product', criteria) %}
{% if debug %}
{{ hook.salesChannelId }}
{% endif %}
Reusable include:
{% macro product_count(criteria) %}
{{ services.repository.search('product', criteria).total }}
{% endmacro %}
Other Paid Twig Administration
Registers Administration component tags for XML-aware template support so custom component tags resolve as known elements.
Administration template:
<sw-card>
<sw-text-field v-model:value="value" />
</sw-card>
Shopware 5
Reports Shopware 5 bootstrap lifecycle methods that omit the expected boolean return value.
Before:
class Shopware_Plugins_Frontend_Acme_Bootstrap extends Shopware_Components_Plugin_Bootstrap
{
public function install()
{
$this->subscribeEvent('Enlight_Controller_Action_PostDispatch', 'onPostDispatch');
}
}
After:
class Shopware_Plugins_Frontend_Acme_Bootstrap extends Shopware_Components_Plugin_Bootstrap
{
public function install()
{
$this->subscribeEvent('Enlight_Controller_Action_PostDispatch', 'onPostDispatch');
return true;
}
}
Reports subscribed event handlers that do not exist yet and offers creating the missing method.
Event subscription:
public static function getSubscribedEvents(): array
{
return [
'Enlight_Controller_Action_PostDispatchSecure_Frontend_Detail' => 'onDetailPage',
];
}
Generated target:
public function onDetailPage(\Enlight_Event_EventArgs $args): void
{
}
Completion Paid Smarty Shopware 5
Completes Shopware 5 Smarty template names, block names, controller actions, and snippet namespaces.
Template and block completion:
{extends file="parent:frontend/detail/index.tpl"}
{block name="frontend_detail_index_buybox"}
{$smarty.block.parent}
{/block}
Completion Paid PHP Shopware 5
Completes Shopware 5 helper calls for plugin config, plugin paths, attributes, and router parameters.
Common Shopware 5 helpers:
$enabled = $this->Config()->get('active');
$pluginPath = $this->Path();
$router->assemble(['controller' => 'detail', 'action' => 'index']);
Completion Paid Json Shopware 5
Completes Shopware plugin metadata keys, compatibility sections, labels, and changelog locales in plugin.json.
plugin.json:
{
"label": {
"en": "Example plugin",
"de": "Beispiel-Plugin"
},
"compatibility": {
"minimumVersion": "5.7.0"
}
}
Completion Paid PHP Shopware 5
Completes Shopware event, hook, and lifecycle names in PHP subscribers and connects them to navigation.
Subscriber map:
public static function getSubscribedEvents(): array
{
return [
'Enlight_Controller_Action_PostDispatchSecure_Frontend_Detail' => 'onDetail',
'sArticles::sGetArticleById::after' => 'afterArticleLoaded',
];
}
Completion Paid JavaScript Shopware 5
Completes classic backend snippets, controller names, and action targets in Shopware JavaScript code.
Backend component code:
Ext.define('Shopware.apps.Acme.view.detail.Window', {
extend: 'Enlight.app.Window',
snippets: {
title: '{s name="window/title"}Title{/s}'
}
});
Linemarker Paid Smarty Shopware 5
Shows navigation markers for Smarty template inheritance, includes, and block relationships.
Template relationships:
{extends file="parent:frontend/detail/index.tpl"}
{include file="frontend/detail/buy.tpl"}
{block name="frontend_detail_index_buybox"}
{$smarty.block.parent}
{/block}
Linemarker Paid PHP Shopware 5
Shows related-target markers from PHP subscriber methods and controller actions to their Shopware event and template context.
Subscriber target:
public static function getSubscribedEvents(): array
{
return [
'Enlight_Controller_Action_PostDispatchSecure_Frontend_Detail' => 'onDetail',
];
}
public function onDetail(\Enlight_Event_EventArgs $args): void
{
}
Linemarker Paid JavaScript Shopware 5
Shows related-target markers for classic backend JavaScript controllers, views, and templates.
Backend view class:
Ext.define('Shopware.apps.Acme.view.detail.Window', {
extend: 'Enlight.app.Window',
alias: 'widget.acme-detail-window'
});
Other Paid PHP Shopware 5
Provides Shopware service ids, service definitions, and default parameters to service-aware IDE features.
Service-aware PHP usage:
$config = Shopware()->Container()->get('config');
$router = Shopware()->Container()->get('router');