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());
}Comparison Table 
| Aspect | Extension Points | Events | 
|---|---|---|
| Purpose | Replace/Extend functionality | Notify about actions | 
| Return Values | Yes (via result property) | No | 
| Flow Control | Yes (via stopPropagation()) | No | 
| Error Handling | Advanced with recovery | Basic | 
| Timing | Pre/during action | Post-action | 
| Use Case | Core functionality | Side effects | 
| Performance Impact | Can be significant | Usually minimal | 
| Complexity | Higher | Lower | 
| Backward Compatibility | Easier to maintain | More 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 
- Use sparingly: Only when you need to replace core functionality
 - Handle errors gracefully: Provide fallback implementations
 - Document thoroughly: Extension points are part of the public API
 - Test extensively: Extension points can break core functionality
 - Consider performance: Extension points can impact performance significantly
 
For Events 
- Keep side effects minimal: Don't perform heavy operations
 - Handle errors gracefully: Don't let event failures break the main flow
 - Use async processing: For heavy operations, use message queues
 - Document side effects: Make it clear what the event does
 - Test in isolation: Events should be testable independently