Skip to content

Extension Points vs Events

Extension Points vs Events ​

Overview ​

Shopware 6 provides two different mechanisms for extending functionality: Extension Points and Events. While they may seem similar, they serve different purposes and have distinct characteristics. Understanding when to use each approach is crucial for effective plugin development.

Key Differences ​

Purpose and Design Philosophy ​

Extension Points ​

  • Purpose: Replace or extend core functionality
  • Design: Result-oriented, flow-controlling
  • Philosophy: "I want to change how this works"

Events ​

  • Purpose: Notify about actions that occurred
  • Design: Notification-based, fire-and-forget
  • Philosophy: "I want to know when this happens"

Return Values and Flow Control ​

Extension Points ​

php
public function onResolveListing(ResolveListingExtension $event): void
{
    // Can return a result that replaces the default behavior
    $event->result = $this->customProductLoader->load($event->criteria, $event->context);

    // Can stop the default implementation
    $event->stopPropagation();
}

Events ​

php
public function onProductCreated(ProductCreatedEvent $event): void
{
    // Cannot return values or control flow
    // Can only perform side effects
    $this->logger->info('Product created: ' . $event->getProduct()->getName());
    $this->notificationService->sendNotification($event->getProduct());
}

Execution Timing ​

Extension Points ​

  • Timing: Before or during the action
  • Purpose: Intercept and modify the process
  • Example: Before product prices are calculated

Events ​

  • Timing: After the action is completed
  • Purpose: React to completed actions
  • Example: After a product has been created

Error Handling ​

Extension Points ​

php
// Built-in error handling with recovery
try {
    $extension->result = $function(...$extension->getParams());
} catch (\Throwable $e) {
    $extension->exception = $e;
    $extension->resetPropagation();

    // Dispatch error event for recovery
    $this->dispatcher->dispatch($extension, self::error($name));

    // If no recovery, rethrow
    if ($extension->result === null) {
        throw $e;
    }
}

Events ​

php
// Basic error handling
public function onProductCreated(ProductCreatedEvent $event): void
{
    try {
        $this->performSideEffect($event);
    } catch (\Exception $e) {
        // Error handling is up to the developer
        $this->logger->error('Failed to process product creation', ['error' => $e->getMessage()]);
    }
}

When to Use Extension Points ​

Use Extension Points when you need to:

1. Replace Core Functionality ​

php
// Replace default product loading with custom logic
public function onResolveListing(ResolveListingExtension $event): void
{
    $event->result = $this->externalProductService->loadProducts($event->criteria);
    $event->stopPropagation();
}

2. Modify Data Before Processing ​

php
// Filter products before they're displayed
public function onProductListing(ProductListingExtension $event): void
{
    $filteredProducts = $this->filterProducts($event->products, $event->context);
    $event->result = $filteredProducts;
    $event->stopPropagation();
}

3. Integrate External Systems ​

php
// Use external pricing service
public function onPriceCalculation(ProductPriceCalculationExtension $event): void
{
    $prices = $this->externalPricingService->calculatePrices($event->products);
    $event->result = $prices;
    $event->stopPropagation();
}

4. Add Conditional Business Logic ​

php
// Apply special pricing for VIP customers
public function onPriceCalculation(ProductPriceCalculationExtension $event): void
{
    if ($this->isVipCustomer($event->context)) {
        $event->result = $this->applyVipPricing($event->products);
        $event->stopPropagation();
    }
}

When to Use Events ​

Use Events when you need to:

1. Send Notifications ​

php
public function onOrderPlaced(OrderPlacedEvent $event): void
{
    $this->emailService->sendOrderConfirmation($event->getOrder());
    $this->smsService->sendOrderNotification($event->getOrder());
}

2. Log Actions ​

php
public function onProductCreated(ProductCreatedEvent $event): void
{
    $this->auditLogger->log('Product created', [
        'productId' => $event->getProduct()->getId(),
        'userId' => $event->getContext()->getUserId()
    ]);
}

3. Update External Systems ​

php
public function onCustomerRegistered(CustomerRegisteredEvent $event): void
{
    $this->crmService->syncCustomer($event->getCustomer());
    $this->analyticsService->trackRegistration($event->getCustomer());
}

4. Trigger Follow-up Actions ​

php
public function onOrderCompleted(OrderCompletedEvent $event): void
{
    $this->inventoryService->reserveStock($event->getOrder());
    $this->shippingService->schedulePickup($event->getOrder());
}

Extension Points vs Service Decoration ​

Before extension points existed, replacing core behavior meant decorating a service. Decoration still works and is still the only option where no extension point is available, but it has structural drawbacks that extension points were introduced to avoid:

AspectExtension PointsService Decoration
Multiple extensions on the same logicIndependent subscribers on the same extension eventNested decorator chain; each link must call the next one
One extension misbehavingAffects its own subscriberBreaks the whole chain and silently drops other extensions' behavior
Adding a parameter to the extended logicExtension object gains a property; existing subscribers keep workingSignature change in every decorator, so a new plugin major per Shopware major
DiscoverabilityExtension classes and event namesRequires inspecting the service container to see who decorates what

Prefer an extension point when one exists for the logic you want to change, and reach for decoration only when it does not. See Decoration in a shared codebase for the rules to follow when you do decorate.

Comparison Table ​

AspectExtension PointsEvents
PurposeReplace/Extend functionalityNotify about actions
Return ValuesYes (via result property)No
Flow ControlYes (via stopPropagation())No
Error HandlingAdvanced with recoveryBasic
TimingPre/during actionPost-action
Use CaseCore functionalitySide effects
Performance ImpactCan be significantUsually minimal
ComplexityHigherLower
Backward CompatibilityEasier to maintainMore complex

Real-World Examples ​

E-commerce Scenarios ​

Product Pricing (Extension Point) ​

php
// Replace default pricing with dynamic pricing from external API
public function onPriceCalculation(ProductPriceCalculationExtension $event): void
{
    $dynamicPrices = $this->pricingApi->getPrices($event->products, $event->context);
    $event->result = $dynamicPrices;
    $event->stopPropagation();
}

Order Notification (Event) ​

php
// Send notifications after an order is placed
public function onOrderPlaced(OrderPlacedEvent $event): void
{
    $this->emailService->sendOrderConfirmation($event->getOrder());
    $this->slackService->notifyTeam($event->getOrder());
}

Product Search (Extension Point) ​

php
// Replace default search with AI-powered search
public function onProductSearch(ProductSearchExtension $event): void
{
    $aiResults = $this->aiSearchService->search($event->query, $event->context);
    $event->result = $aiResults;
    $event->stopPropagation();
}

Inventory Update (Event) ​

php
// Update an external inventory system after a product update
public function onProductUpdated(ProductUpdatedEvent $event): void
{
    $this->inventoryService->syncProduct($event->getProduct());
}

Migration from Events to Extension Points ​

If you're currently using Events for functionality replacement, consider migrating to Extension Points:

Before (Event-based) ​

php
// Old approach - using events for functionality replacement
public function onProductListingCriteria(ProductListingCriteriaEvent $event): void
{
    // Modify criteria
    $event->getCriteria()->addFilter(new EqualsFilter('active', true));
}

public function onProductLoaded(ProductLoadedEvent $event): void
{
    // Post-process products
    foreach ($event->getProducts() as $product) {
        $product->addExtension('customData', $this->getCustomData($product));
    }
}

After (Extension Point-based) ​

php
// New approach - using extension points for functionality replacement
public function onResolveListing(ResolveListingExtension $event): void
{
    // Replace entire listing resolution
    $event->result = $this->customListingService->resolve($event->criteria, $event->context);
    $event->stopPropagation();
}

Best Practices ​

For Extension Points ​

  1. Use sparingly: Only when you need to replace core functionality
  2. Handle errors gracefully: Provide fallback implementations
  3. Document thoroughly: Extension points are part of the public API
  4. Test extensively: Extension points can break core functionality
  5. Consider performance: Extension points can impact performance significantly

For Events ​

  1. Keep side effects minimal: Don't perform heavy operations
  2. Handle errors gracefully: Don't let event failures break the main flow
  3. Use async processing: For heavy operations, use message queues
  4. Document side effects: Make it clear what the event does
  5. Test in isolation: Events should be testable independently
Was this page helpful?
UnsatisfiedSatisfied
Be the first to vote!
0.0 / 5  (0 votes)