Building robust extensions in Shopware requires more than just writing code; it demands a deep understanding of how the platform manages state, dependencies, and configuration changes over time. In Shopware 6.7, the plugin lifecycle has been refined to support higher performance standards, stricter type safety, and smoother upgrade paths. This guide explores the internal mechanics of the Shopware 6 plugin lifecycle, ensuring your extensions integrate seamlessly with the latest platform capabilities.

The Blueprint: plugin.xml

Every extension's journey begins with plugin.xml. In Shopware 6.7, this file is still the source of truth for metadata, but it enforces stricter validation during installation. It defines the plugin's name, code, version, and dependencies. Crucially, it signals to the kernel which services should be loaded and where migrations reside.

<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/shopware/platform/main/src/Core/Plugin/plugin.xsd">
    <name>MyLifecyclePlugin</name>
    <code>mylecycleplugin</code>
    <vendor>swag</vendor>
    <version>1.0.0</version>
    <description>A plugin explaining the lifecycle in Shopware 6.7.</description>

    <pluginFromPackage>false</pluginFromPackage>
    <compatibleVersion>v2.17.0|v2.18.0|v6.0.0|v6.1.0|v6.2.0|v6.3.0|v6.4.0|v6.5.0|v6.6.0|v6.7.0</compatibleVersion>
    
    <autoRun>
        <name>CreateMigrations</name>
        <minShopwareVersion>6.4.0</minShopwareVersion>
        <maxShopwareVersion>6.999.0</maxShopwareVersion>
    </autoRun>
</plugin>

In Shopware 6.7, the compatibleVersion range ensures backward compatibility checks are robust. The autoRun block is critical for migrations, ensuring they execute even if the plugin isn't manually activated immediately upon installation via the CLI.

Initialization and Activation

When a plugin is installed or activated, the PluginManager registers the extension in the database and triggers the kernel to rebuild its container. The lifecycle phase here focuses on dependency resolution.

The core logic resides in src/Shopware/MyLifecyclePlugin/ShopwareMyLifecyclePlugin.php. While older patterns allowed heavy logic here, Shopware 6.7 encourages a lean plugin class that primarily delegates work to services.

declare(strict_types=1);

namespace Shopware\MyLifecyclePlugin;

use Shopware\Core\Framework\Plugin;
use Shopware\Core\Framework\PluginException;
use Shopware\Core\Framework\Plugin\HttpKernel\Exception\KernelShutdownHandlerInterface;

class ShopwareMyLifecyclePlugin extends Plugin implements KernelShutdownHandlerInterface
{
    public function activate(): void
    {
        parent::activate();
        // Activation logic should be minimal. 
        // Use Events or CLI commands for heavy lifting.
    }

    public function deactivate(): void
    {
        parent::deactivate();
        // Avoid long-running tasks here; they block the UI.
    }
}

Shopware 6.7 Insight: The platform now utilizes KernelShutdownHandlerInterface for graceful shutdowns during deactivation or updates. Implementing this interface ensures that temporary resources (like cache entries or open connections) are cleaned up reliably without leaving orphaned state.

Database Evolution: Migrations

The database lifecycle is distinct from the code lifecycle. Shopware 6.7 relies on Doctrine migrations, versioned by a timestamp prefix. The plugin lifecycle triggers migration execution via the CreateMigrations auto-run action defined in plugin.xml.

Migrations must handle both schema changes and data transformations safely.

<?php declare(strict_types=1);

namespace Shopware\MyLifecyclePlugin\Migration;

use Doctrine\DBAL\Schema\Schema;
use Shopware\Core\Framework\Migration\MigrationStep;

class Migration1705000000 extends MigrationStep
{
    public function getHash(): string
    {
        return 'Migration1705000000';
    }

    public function update(Connection $connection): void
    {
        $sql = <<<SQL
            CREATE TABLE IF NOT EXISTS my_lifecycle_config (
                id BINARY(16) NOT NULL,
                configuration_data JSON NOT NULL,
                PRIMARY KEY (id)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
        SQL;

        $connection->executeStatement($sql);
    }

    public function updateDestructive(Connection $connection): void
    {
        // Only execute destructive changes if strictly necessary.
        // Shopware 6.7 strongly discourages data loss during updates.
    }
}

Best Practice: In Shopware 6.7, always use updateDestructive sparingly. The platform's upgrade process runs in multiple steps. Destructive migrations are only executed when the target version matches exactly or during major schema resets. Prefer idempotent updates using getVersion() checks if you must modify existing data.

Service Registration and Dependency Injection

Once the plugin is active, its services become part of the global container. The lifecycle ensures service definitions are loaded via services.xml. Shopware 6.7 enforces strict service visibility and requires explicit tag declarations.

<service id="Shopware\MyLifecyclePlugin\EventListener\CartListener">
    <tag name="shopware.event_listener" event="cart.loaded" priority="100"/>
</service>

<service id="Shopware\MyLifecyclePlugin\Service\LifecycleChecker">
    <call method="setContainer">
        <argument type="service" id="service_container"/>
    </call>
</service>

Key Mechanism: The compiler pass in Shopware 6.7 optimizes service tags at container dump time. When the plugin is activated, the cache is cleared, forcing a rebuild of the service map. This ensures your listeners and repositories are available immediately. In production environments, this happens asynchronously to avoid request timeouts during large updates.

Frontend Asset Lifecycle

For extensions modifying the storefront or administration, the asset lifecycle involves compilation. Shopware 6.7 introduces improvements in the Admin Extension SDK's hot reload capabilities, but for production, assets must be compiled via CLI.

bin/storefront:build
bin/administration:build

The plugin should not attempt to write files directly to the build directories during runtime. Instead, asset sources reside in src/Resources/public/ or Administration/, and the compiler copies and minifies them during the build process. This separation ensures that your code runs in development mode with hot reload while shipping optimized bundles in production.

Updates and Version Management

The most critical lifecycle phase is the upgrade. In Shopware 6.7, plugins must support incremental updates. If a user upgrades from v1.0.0 to v2.0.0, every migration version between those timestamps must execute successfully.

To manage semantic versions, you should implement a version checker that aligns with Shopware's platform versioning strategy. Avoid breaking API contracts exposed by your plugin. Use UpgradeEvents to migrate configuration data from old settings rows to new entities:

use Shopware\Core\Framework\Plugin\Exception\RequirementsNotSupportedException;

// In PluginService.php or similar
private function checkCompatibility(): void
{
    // Shopware 6.7 requires PHP 8.1+; ensure your plugin reflects this
    if (version_compare(PHP_VERSION, '8.1.0', '<')) {
        throw new RequirementsNotSupportedException('PHP 8.1 or higher is required.');
    }
}

Common Pitfalls in Shopware 6.7

  1. Cache Staleness: Failing to clear the cache after code changes in development. In 6.7, the profiler helps visualize container rebuilds; watch for "Container rebuilt" logs during updates.
  2. Service Tagging Errors: Missing tags or incorrect event names will silently fail. Use bin/console lint:container to validate service definitions locally.
  3. Hardcoded Version Checks: Relying on specific Shopware minor versions rather than semantic compatibility ranges can break your plugin as the ecosystem evolves.

Conclusion

The Shopware 6.7 plugin lifecycle is a robust orchestration of metadata validation, dependency injection, schema management, and asset compilation. By adhering to modern coding standards, leveraging KernelShutdownHandlerInterface, and respecting the separation between build-time and runtime, developers can create extensions that are performant, secure, and upgrade-proof. As Shopware 6.7 continues to evolve, focusing on these lifecycle fundamentals will ensure your plugin remains compatible and efficient for years to come.

Mastering this lifecycle transforms extension development from a series of hacks into an architectural discipline, empowering you to build enterprise-grade commerce solutions on the Shopware platform.