Registering a plugin in Shopware 6 is the foundational step that bridges your custom code with the platform’s core ecosystem. While earlier versions relied heavily on procedural hooks, manual service modification, and XML-heavy configurations, Shopware 6 introduced a modern, composer-driven architecture that dramatically simplifies extension management. With the release of version 6.7, the registration process has become even more streamlined, leveraging native PHP 8 attributes, automatic service discovery, and a strictly defined plugin lifecycle. This guide walks you through the exact steps to properly register a plugin in Shopware 6.7 and beyond, ensuring your extension integrates seamlessly with the framework’s dependency injection container, event system, and activation workflow.

The Registration Philosophy in Shopware 6.7

In Shopware 6, plugins are no longer just directories dropped into custom/plugins/. They are composer-managed packages that follow strict structural conventions. The registration process is fundamentally about informing the Shopware kernel where your plugin lives, what metadata it carries, and how it should participate in the application boot sequence. When Shopware boots, it uses an internal PluginLoader to scan all composer repositories for packages marked with the shopware-platform/plugin type. Valid plugins are automatically collected, their namespaces resolved, and their lifecycle methods queued for execution. Your role as a developer is not to manually register your extension in the kernel, but to provide the correct entry points and metadata that allow this automated discovery to succeed.

Step 1: Establishing the Composer & Directory Foundation

Registration begins before Shopware even boots. Every valid plugin must reside in a directory that composer can autoload. Create a new folder (e.g., custom/plugins/YourExtensionName/) and ensure it contains a properly configured composer.json. The file must declare your package type, namespace, and autoloading rules:

{
  "name": "your-vendor/your-extension",
  "type": "shopware-platform/plugin",
  "autoload": {
    "psr-4": { "YourVendor\\YourExtension\\": "src/" }
  },
  "extra": {
    "shopware-plugin-class": "YourVendor\\YourExtension\\YourPlugin"
  }
}

The type field signals the platform to treat this as an extension. The extra.shopware-plugin-class key explicitly points to your entry point file. Shopware 6.7 enforces stricter validation here, so ensure your namespace exactly matches your folder structure. Once defined, run composer update in your project root so the kernel recognizes the new package during installation.

Step 2: Crafting the Plugin.php Entry Point

The heart of registration lies in Plugin.php, located at your plugin’s root directory. This file must extend Shopware\Core\Framework\Plugin\AbstractPlugin. It acts as the official bridge between Shopware’s boot sequence and your custom logic. Below is a minimal, v6.7-compliant implementation:

<?php declare(strict_types=1);

namespace YourVendor\YourExtension;

use Shopware\Core\Framework\Plugin;

final class YourPlugin extends Plugin
{
    public function boot(): \Closure
    {
        return function () {
            // Deferred initialization logic
        };
    }

    public function install(): void
    {
        // Database structures, default configurations, or admin UI setup
    }
}

Notice the boot() method returns a closure. Shopware 6.7 delays execution until the dependency injection container is fully initialized, preventing premature service resolution and reducing memory overhead. Use this hook for registering event subscribers, attaching routes, or modifying service definitions. The install() method executes only when the plugin is first activated via the administration panel or CLI command.

Step 3: Modern Service & Route Discovery in v6.7+

One of the most significant shifts in Shopware 6.7 is native support for PHP attributes. XML service configurations are largely deprecated in favor of lightweight, IDE-friendly attribute syntax. If your plugin needs to register an event subscriber, you can now do it directly on the class:

namespace YourVendor\YourExtension\EventSubscriber;

use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem;
use Shopware\Core\Framework\Routing\RouteScope;

#[AsTaggedItem('kernel.event_subscriber')]
final class CartPriceSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [CartRulesEvent::class => 'onCartRules'];
    }

    public function onCartRules(CartRulesEvent $event): void
    {
        // Custom pricing logic
    }
}

The #[AsTaggedItem] attribute automatically tags the class during container compilation. No XML wiring required. Similarly, route registration can now use Symfony-style routing attributes combined with Shopware’s route scope markers:

use Shopware\Core\Framework\Routing\RouteScope;
use Symfony\Component\Routing\Attribute\Route;

#[RouteScope(scopes: [RouteScope::SCOPE_STOREFRONT])]
#[Route('/custom/endpoint', name:'frontend.custom.endpoint', methods:['GET'])]
final class CustomController extends ControllerBase { /* ... */ }

This approach aligns with modern PHP standards, improves autocompletion, and reduces configuration drift between local development and production environments.

Step 4: Activation Workflow & Lifecycle Awareness

Registration is only half the process; activation completes the integration. After composer updates and file placement, navigate to Administration → Marketplace → Plugins. Your extension should appear in the list. Click Install, then Activate. During activation, Shopware executes install(), generates routing caches, compiles the service container, and registers admin bundles if applicable.

Always clear the cache afterward (bin/console cache:clear). In v6.7, the platform uses a dual-cache strategy (HTTP and application cache), so skipping this step often results in phantom errors where routes or services fail to resolve despite correct code.

Best Practices & Common Pitfalls

  • Namespace Mismatches: PSR-4 autoloading is case-sensitive and path-strict. A single folder misalignment breaks discovery.
  • Manual Kernel Modifications: Never alter Kernel.php directly. Let the framework handle lifecycle management through standardized hooks.
  • Overusing boot(): Heavy database queries or container services should live in install() or dedicated CLI commands. Keep boot() lightweight and closure-based.
  • Ignoring Composer Reloads: Plugin structure changes require composer dump-autoload to sync with the platform’s registry.

Conclusion

Registering a plugin in Shopware 6.7 is less about manual configuration and more about adhering to platform conventions. By structuring your composer package correctly, respecting the closure-based boot sequence, and leveraging PHP 8 attributes for service discovery, your extension will integrate smoothly into the ecosystem. The framework’s shift toward automated detection and modern dependency injection not only simplifies development but also future-proofs your code against upcoming architectural updates. Stick to official templates, embrace attribute-driven registration, and let Shopware’s kernel handle the heavy lifting while you focus on delivering value through your business logic.