Shopware Decoration Best Practices: Override One Method, Keep Others Untouched
Learn the golden rule of Shopware decorations - how to efficiently override only what you need while letting PHP inheritance handle the rest automatically.

Die Goldene Regel für Shopware 6 Decorations: Überschreibe nur die Methode, die du ändern musst. PHP's Vererbung handhabt automatisch alle anderen.
Die Grundregeln
- ✅ Überschreibe NUR die Methode, die du ändern musst
- ✅ Implementiere immer
getDecorated() - ✅ Decorated Service ist ERSTES Constructor-Argument
- ✅ Nutze
$this->getDecorated()->method()für die Kette - ✅ Lass Parent-Klasse alle anderen Methoden automatisch handhaben
❌ Falsch - Alles überschreiben
class ProductListingDecorator extends AbstractProductListingRoute
{
// ❌ Unnötig: Nur Delegation
public function getName(): string
{
return $this->getDecorated()->getName();
}
// ✅ Das brauchen wir wirklich
public function load(string $categoryId, Request $request, SalesChannelContext $context, Criteria $criteria): ProductListingResult
{
$result = $this->getDecorated()->load($categoryId, $request, $context, $criteria);
$this->customService->enhance($result);
return $result;
}
}
✅ Richtig - Nur das Nötige
class ProductListingDecorator extends AbstractProductListingRoute
{
private AbstractProductListingRoute $decorated;
private MyCustomService $customService;
public function __construct(AbstractProductListingRoute $decorated, MyCustomService $customService)
{
$this->decorated = $decorated;
$this->customService = $customService;
}
public function getDecorated(): AbstractProductListingRoute
{
return $this->decorated;
}
// Nur die eine Methode überschreiben
public function load(string $categoryId, Request $request, SalesChannelContext $context, Criteria $criteria): ProductListingResult
{
$result = $this->getDecorated()->load($categoryId, $request, $context, $criteria);
foreach ($result->getProducts() as $product) {
$this->customService->enhance($product);
}
return $result;
}
// getName(), getCriteria() etc. automatisch durch Parent-Klasse
}
Constructor-Patterns
Einfach (nur Decorated Service)
class SimpleCartDecorator extends AbstractCartPersister
{
private AbstractCartPersister $decorated;
public function __construct(AbstractCartPersister $decorated)
{
$this->decorated = $decorated;
}
public function save(Cart $cart, SalesChannelContext $context): void
{
if ($cart->getLineItems()->count() > 100) {
throw new \RuntimeException('Zu viele Items im Warenkorb');
}
$this->getDecorated()->save($cart, $context);
}
}
Mit Dependencies
class AdvancedCartDecorator extends AbstractCartPersister
{
public function __construct(
AbstractCartPersister $decorated, // Erstes Argument!
LoggerInterface $logger,
SystemConfigService $systemConfig
) {
$this->decorated = $decorated;
$this->logger = $logger;
$this->systemConfig = $systemConfig;
}
}
Häufige Fehler
❌ Decorated Service nicht aufrufen
// FALSCH: Original-Logik geht verloren
public function load(string $id): ProductEntity
{
return new ProductEntity(); // Decorated nie aufgerufen!
}
// RICHTIG: Immer decorated aufrufen
public function load(string $id): ProductEntity
{
$product = $this->getDecorated()->load($id);
$this->enhance($product);
return $product;
}
❌ getDecorated() vergessen
// FALSCH: Decoration-Chain kaputt
public function doSomething(): void
{
$this->decorated->doSomething(); // Direkter Aufruf
}
// RICHTIG: Über getDecorated()
public function doSomething(): void
{
$this->getDecorated()->doSomething();
}
Template
class MyDecorator extends AbstractOriginalService
{
private AbstractOriginalService $decorated;
public function __construct(AbstractOriginalService $decorated)
{
$this->decorated = $decorated;
}
public function getDecorated(): AbstractOriginalService
{
return $this->decorated;
}
// Nur überschreiben was nötig ist
public function theMethodToChange(): ReturnType
{
$result = $this->getDecorated()->theMethodToChange();
// Deine Logik
return $result;
}
}
services.xml:
<service id="MyPlugin\Service\MyDecorator" decorates="Original\Service">
<argument type="service" id="MyPlugin\Service\MyDecorator.inner"/>
<!-- Weitere Dependencies -->
</service>
Warum funktioniert das?
PHP Vererbungskette:
Dein Decorator → Abstract Class → Decorated Service
Wenn eine nicht-überschriebene Methode aufgerufen wird, delegiert die Abstract Class automatisch an getDecorated().
Vorteile:
- Weniger Code = weniger Bugs
- Automatische Kompatibilität bei Shopware-Updates
- Bessere Performance durch weniger Methodenaufrufe
- Einfachere Wartung
Enjoyed this?
Get new posts as they land.
Keep reading

531 Requests on a Homepage — Cutting a Headless Storefront Down to 101
A headless Shopware 6 + Nuxt storefront loaded 531 requests on its mobile homepage and scored 56 in Lighthouse. After one batch of changes on production it was 101 requests and 88. How I measured, where the weight came from, what I reverted, and a checklist for your own storefront.

Shareable Product Configurator in Shopware 6 — State in the URL
How I built a product configurator for a Shopware 6 storefront where the whole state lives in a ?cfg= URL parameter — what to serialize, how the PDF export works, what breaks, and the trade-offs of letting users edit a shared configuration.

Shopware 6 Meets AI Agents - What an MCP Server for Your Shop Could Do
MCP gives AI agents a standard way to talk to real systems. Here's what an MCP server over Shopware's Admin API could look like - the tool surface, the architecture, and the honest risks of letting an agent near a live shop.