Skip to content
shopware·26.10.2025·2 min read

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.

Shopware Decoration Best Practices: Override One Method, Keep Others Untouched

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.

Subscribe via RSS

Keep reading