Shopware 6 is renowned for its flexibility and robust architecture, but its true power lies in extensibility. Whether you are a merchant needing custom business logic or a developer building solutions for the marketplace, creating plugins is an essential skill. This guide walks you through crafting your first plugin, optimized for Shopware 6.7, where modern PHP standards, improved performance, and stricter validation requirements define the development experience.

Prerequisites and Environment

Before coding, ensure your environment meets Shopware 6.7 requirements:

  • PHP 8.2 or higher (PHP 8.3 is recommended).
  • Composer installed globally.
  • A functional Shopware 6.7 installation with write permissions for custom/plugins.

In Shopware 6.7, the ecosystem enforces stricter type declarations and security layers. Familiarity with Symfony components, Dependency Injection, and PHP Attributes is highly beneficial, as XML configurations are increasingly replaced by code-first approaches.

Step 1: Scaffolding Your Plugin

Shopware provides a dedicated Composer package to generate plugin skeletons that adhere to best practices. Navigate to your installation's custom/plugins directory and run:

composer create-shopware/plugbase YourVendor/YourPlugin

The generator prompts for metadata (name, description, author). These details populate the plugin store listing and admin UI later. Upon completion, you will receive a folder structure that follows PSR-4 autoloading standards.

Key Tip: Run composer dump-autoload after generation to ensure class maps are updated. In Shopware 6.7, the autoloader is more aggressive about caching; missing classes here are a common cause of fatal errors during activation.

Step 2: Understanding the Directory Structure

Shopware 6 relies on convention over configuration for plugin directories. Proper placement determines how your code is loaded by the kernel.

  • src/: The heart of your logic. Code here is auto-scanned and registered if namespaced correctly.
  • src/Storefront/: Controllers and templates mapped to the storefront theme namespace.
  • src/Administration/: Vue.js components for admin extensions. Use the standard Vue folder structure (static/src/vue/...).
  • src/Core/: Business logic, services, and entities. Services defined here are auto-registered in the DI container via Symfony Flex conventions.
  • Resources/config/: Configuration files. While XML is still supported, Shopware 6.7 encourages using PHP attributes for routing and configuration where possible.
  • translations/: Required for localization. Even if your plugin is English-only, include en-GB and de-DE folders to avoid admin warnings.

Step 3: Plugin Registration and Metadata

Open YourPlugin.php. This class extends \Shopware\Core\Framework\Plugin and is the bridge to the Shopware Kernel.

<?php declare(strict_types=1);

namespace YourVendor\YourPlugin;

use Shopware\Core\Framework\Plugin;

class YourPlugin extends Plugin
{
    // Constructor logic or upgrade/downgrade hooks can go here
}

In Shopware 6.7, the your-plugin.php file serves as the plugin store manifest. It defines translations and compatibility data. Ensure your composer.json includes:

  1. A valid license file in the root (LICENSE.md).
  2. The "shopware-platform-plugin": true type (if applicable).
  3. Correct package name matching the folder structure.

The Store validates these files strictly during submission. Missing a license or having incorrect autoloaders will cause rejection even before activation.

Step 4: Adding Your First Functionality

Let's add a custom Store API endpoint and an event subscriber to demonstrate modern patterns.

Custom Controller with Attributes

Create src/Controller/Frontend/YourController.php. Shopware 6.7 fully supports PHP attributes for routing, reducing boilerplate.

<?php declare(strict_types=1);

namespace YourVendor\YourPlugin\Controller;

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
use Shopware\Core\Framework\Routing\Annotation\RouteScope;
use Shopware\Storefront\Controller\StorefrontController;

#[RouteScope(scopes: ['store-api'])]
class YourController extends StorefrontController
{
    #[Route(path: '/api/your-plugin/info', name: 'api.action.your_plugin.info', methods: ['GET'])]
    public function getInfo(): JsonResponse
    {
        // Logic here, e.g., fetching configuration or calling services
        return new JsonResponse([
            'status' => 'success',
            'message' => 'Hello from Shopware 6.7 Plugin!',
            'timestamp' => time()
        ]);
    }
}

Important: The #[RouteScope(scopes: ['store-api'])] attribute is mandatory for API endpoints to ensure the security context is correctly applied. In 6.7, missing this scope may result in access denied errors or routing mismatches due to stricter route parsing.

Event Subscription

To extend core behavior, implement an EventSubscriberInterface. For example, listen to cart calculation events:

<?php declare(strict_types=1);

namespace YourVendor\YourPlugin\EventListener;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Shopware\Core\Checkout\Cart\Event\Cart CalculatedEvent;

class CartModificationSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            // Use the specific event class name for 6.7 compatibility
            'cart.calculated' => 'onCartCalculated',
        ];
    }

    public function onCartCalculated(CartCalculatedEvent $event): void
    {
        // Access cart, modify line items, or add custom logic
        $cart = $event->getCart();
        // Example: Log a message for debugging
        error_log('Cart calculated by YourPlugin');
    }
}

Ensure this class resides in src/EventListener. Shopware's DI container auto-wires event subscribers automatically. No XML service registration is needed if the namespace matches the default discovery rules.

Step 5: Shopware 6.7 Specifics and Best Practices

Shopware 6.7 introduces nuances that developers must respect:

  1. PHP Standards Compliance: The Plugin Store CI pipeline now runs php-cs-fixer checks. Your code must adhere to PSR-12 and Shopware's coding standards. Use the bin/php-cs-fixer fix command locally before committing changes intended for the store.
  2. Deprecation Handling: 6.7 removes legacy APIs used in earlier versions. Check the release notes for deprecated services. For instance, direct usage of $this->container->get() is discouraged; always use constructor injection or request attributes.
  3. Security Layers: API controllers must respect rate limiting and permission scopes. Avoid bypassing the StorefrontController context unless building a backend-only extension. Use #[RouteScope] correctly to prevent routing conflicts.
  4. Migration Classes: If your plugin installs database tables, use Shopware\Core\Framework\Migration\Migration classes. In 6.7, migrations are versioned and applied atomically. Place them in src/Migration/ following the naming convention Migration123456YourTable.php.

Step 6: Installation, Building, and Testing

Once your code is ready, prepare the plugin for use:

  1. Build Assets: If you added JavaScript or CSS, run bin/build-plugin.sh. This compiles Vue files and minifies assets.
  2. Activate:
    bin/plugin activate YourVendor/YourPlugin
    bin/plugin refresh
    
    The refresh command updates the routing cache and event maps. In 6.7, this is critical; skipping it may cause "route not found" errors due to cached kernel configs.
  3. Debugging: Check /var/log/shopware.log for errors. Use bin/plugin status YourVendor/YourPlugin to verify installation state. For API testing, use tools like Postman against your new endpoint to ensure responses are valid JSON and respect CORS policies.

Conclusion

You have successfully created a Shopware 6 plugin compatible with version 6.7. By leveraging the scaffold generator, understanding the directory conventions, and adhering to modern PHP attributes and security scopes, you've laid a solid foundation for extensibility.

As you expand your plugin, consider adding administration UI components via Vue.js or exploring deep integrations using Shopware's event system. The ecosystem is evolving rapidly, so always consult the official Shopware documentation and GitHub repositories for the latest patterns. With these tools in hand, you are ready to build powerful extensions that enhance the Shopware experience for merchants worldwide.

Happy coding!