Last Updated on January 21, 2022

In this article, we will create a sample plugin for Shopware 6. We will create a plugin through a minimal process, then after that will manually set up structures in Shopware6.

Minimal Plugin Setup:

After installing Shopware 6, go to directory path and run the command:

php bin/console plugin:create EmizentechPlugin

Now, go to Admin and select Settings > System > Plugins, where the new plugin Emizentech is listed.

Shopware 6 Plugin In Adding Config Settings:

Go to plugin directory path “custom/plugins/EmizentechPlugin/src/Resources/config”
And create a config.xml file manually.

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/shopware/platform/master/src/Core/System/SystemConfig/Schema/config.xsd">
    <card>
        <title>EmizentechPlugin settings</title>

    </card>
</config>

Shopware 6 Plugin In Adding Service Settings:

EmizentechService for now, and we will also put it in the Service subdirectory. So, emizentech service setup in src/Service/EmizentechService.php

<?php

namespace EmizentechPlugin\Service;

class EmizentechService
{

    public function emizentechMethod ($variable)
    {
        var_dump($variable);
    }

}

Also adding a line of code in src/Resources/config/services.xml file, containing the new service:

<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>

        <service id="EmizentechPlugin\Service\EmizentechService">

        </service>

    </services>
</container>

Shopware 6 Plugin In Adding Event Subscriber:

The event subscriber is the most useful feature in Shopware 6. This allows you to trigger a code if a specific event happens. A standard name and location for the new PHP class would be src/Subscriber/Subscriber.php

<?php

namespace EmizentechPlugin\Subscriber;

use Shopware\Storefront\Page\Checkout\Cart\CheckoutCartPageLoadedEvent;
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent;
use Shopware\Storefront\Page\Product\ProductPageLoadedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class Subscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            ProductPageLoadedEvent::class => 'onProductPageLoaded',
            CheckoutCartPageLoadedEvent::class  => 'onCheckoutPageLoaded',
            CheckoutConfirmPageLoadedEvent::class => 'onCheckoutConfirmPageLoaded'
        ];
    }

    /**
     * Handles the stuff, that happens on the product detail page
     *
     * @param ProductPageLoadedEvent $event
     */
    public function onProductPageLoaded (ProductPageLoadedEvent $event)
    {
        //do something when the product detail page loads
    }

    /**
     * Calls onCheckoutPagesRefresh for handling stuff in all checkout pages
     *
     * @param CheckoutConfirmPageLoadedEvent $event
     */
    public function onCheckoutConfirmPageLoaded(CheckoutConfirmPageLoadedEvent $event) : void
    {
        $this->onCheckoutPagesRefresh($event);
    }

    /**
     * Calls onCheckoutPagesRefresh for handling stuff in all checkout pages
     *
     * @param CheckoutCartPageLoadedEvent $event
     */
    public function onCheckoutPageLoaded(CheckoutCartPageLoadedEvent $event) : void
    {
        $this->onCheckoutPagesRefresh($event);
    }

    /**
     * Handles the stuff, that happens in both checkout pages
     *
     * @param CheckoutCartPageLoadedEvent|CheckoutConfirmPageLoadedEvent $event
     */
    private function onCheckoutPagesRefresh ($event)
    {
        //do something in the checkout

    }

}

And also adding a line of code in services.xml file, containing the new subscriber:

<service id="EmizentechPlugin\Subscriber\Subscriber">
            <argument type="service" id="Symfony\Contracts\EventDispatcher\EventDispatcherInterface"/>
            <tag name="kernel.event_subscriber"/>
        </service>

Shopware 6 Plugin In Adding A Command:

Shopware 6, allows us not just to run some commands but also to create our own. The basic command setup is not very complicated. We just need to create a class, that extends the Command class and has the two most important methods.

The first one is the “configure” method. It is not mandatory, but most of the time we need it anyway, because it contains the options and arguments for our command.

Whereas, the second method must be named “execute” because it is automatically called when you run the command from the console.

We will name our command EmizentechCommand and put it into src/Command directory.

src/Comman/EmizentechCommand.php

<service?php

namespace EmizentechPlugin\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class EmizentechCommand  extends Command
{
    const ARG_NAME = 'argument';
    const OPT_NAME = 'option';

    protected static $defaultName = 'Emizentechplugin:Emizentechcommand';

    protected function configure()
    {
        $this->addArgument(self::ARG_NAME, InputArgument::OPTIONAL, 'This is an optional argument.');
        $this->addOption(self::OPT_NAME, null, InputOption::VALUE_OPTIONAL, 'This is an optional option.');
    }

    protected function execute(InputInterface $input, OutputInterface $output) : int
    {
        //get the arguments
        $arguments = $input->getArguments();

        //write a line to the console
        $output->writeln('Emizentech command works.');

        //return success code
        return 1;
    }

}

And, also need to add an entry to the services.xml file and tag.

 <service id="EmizentechPlugin\Command\EmizentechCommand">
            <tag name="console.command" command="emizentechplugin:emizentechcommand"/>
        </service>

Shopware 6 Plugin In Adding A Controller:

A controller is a class that receives requests and handles them. There are two most commonly used types of controllers – storefront controllers and backend controllers.

First of all, we will add the storefront controller itself: src/Storefront/Controller/EmizentechController.php

<?php

namespace EmizentechPlugin\Storefront\Controller;

use Shopware\Core\Framework\Routing\Annotation\RouteScope;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Storefront\Controller\StorefrontController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

/**
 * @RouteScope(scopes={"storefront"})
 */
class EmizentechController extends StorefrontController
{
    /**
     * @Route("/Emizentech", name="frontend.Emizentechplugin.Emizentech", methods={"GET"})
     */
    public function showPage(Request $request, SalesChannelContext $context): Response
    {
        return $this->renderStorefront('@EmizentechPlugin/storefront/page/Emizentech/index.html.twig', [
            'customParameter' => 'Custom parameter value'
        ]);
    }
}

It resides in the src/Storefront/Controller directory and is called EmizentechController. It is a class, that extends the Shopware 6 core class Shopware\Storefront\Controller\StorefrontController.

In case you needed a controller for the backend, you should extend Symfony\Bundle\FrameworkBundle\Controller\AbstractController class.

Our basic storefront controller contains one method, that gets called when the URL /emizentech is loaded in the user’s browser. To make this work, we need to add the /src/Resources/config/routes.xml file to our plugin’s structure:

<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/routing
        https://symfony.com/schema/routing/routing-1.0.xsd">
    <import resource="../../Storefront/Controller/**/*Controller.php" type="annotation" />
</routes>

Now, we need to make sure that the controller is registered in the system, it is made public, and has the dependency injection container set to it. As usual, this is done in the services.xml:

      <service id="EmizentechPlugin\Storefront\Controller\EmizentechController" public="true">
            <call method="setContainer">
                <argument type="service" id="service_container"/>
            </call>
        </service>

One last thing is left to be added, and we are ready to test the controller on the URL address /emizentech on our Shopware 6 store. And this is precisely what our storefront controller is doing – rendering a page using a template.

In our case, it is a sample twig template, named index.html.twig, located in the src/Resources/views/storefront/page/emizentech directory:

{% sw_extends '@Storefront/storefront/base.html.twig' %}
{% block base_content %}
    <h1>Emizentech controller works!</h1>
    {{ dump() }}
{% endblock %}

Shopware 6 Plugin In Adding A Migration:

As we want our plugin to be portable, that’s why we will add this code to the migration and make it a part of our plugin. Migration is a class that we can create manually like any other but, it is easier to use a command that is made specifically for this purpose. The command looks like this:

php bin/console database:create-migration -p EmizentechPlugin –name EmizentechEntity

We will create a PHP file such as this in the src/Migration directory within our plugin:

<?php declare(strict_types=1);

namespace EmizentechPlugin\Migration;

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

class Migration1621430431EmizentechEntity extends MigrationStep
{
    public function getCreationTimestamp(): int
    {
        return 1621430431;
    }

    public function update(Connection $connection): void
    {

        $connection->executeUpdate("
            CREATE TABLE IF NOT EXISTS `emizentech_table` (
                `id` BINARY ( 16 ) NOT NULL,
                `name` VARCHAR ( 255 ) NOT NULL COMMENT 'Name',
                `custom_fields` json DEFAULT NULL,
                `created_at` datetime(3) NOT NULL,
                `updated_at` datetime(3) DEFAULT NULL,
                PRIMARY KEY ( `id` )
            ) ENGINE = INNODB DEFAULT CHARSET = utf8;
        ");

    }

    public function updateDestructive(Connection $connection): void
    {
        // implement update destructive
    }
}

This migration will be executed in the background when you will install your plugin. So, make sure you remove or edit it before you install your plugin because every migration will be executed just once!

Now go to admin Settings > System > Plugins and install plugin After installation click on activate:

Through the command line let’s check whether this plugin is working or not:

php bin/console emizentechplugin:emizentechcommand
And, this is how the command is executed, its output looks like “Emizentech command works”:

Hire Shopware developers

Shopware 6 Plugin In Adding An Entity:

Let us create the entity definition first. This is where you will want to put the name of your table. The same name will be used when you will use the table as a repository.

Creating a subdirectory “EmizentechPlugin/src/Core/Content/EmizentechEntity” and create Entity definition file EmizentechEntityDefinition.php

<?php declare(strict_types=1);
namespace EmizentechPlugin\Core\Content\EmizentechEntity;
use Shopware\Core\Framework\DataAbstractionLayer\Field\CreatedAtField;
use Shopware\Core\Framework\DataAbstractionLayer\Field\UpdatedAtField;
use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition;
use Shopware\Core\Framework\DataAbstractionLayer\Field\CustomFields;
use Shopware\Core\Framework\DataAbstractionLayer\Field\IdField;
use Shopware\Core\Framework\DataAbstractionLayer\Field\StringField;
use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\PrimaryKey;
use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\Required;
use Shopware\Core\Framework\DataAbstractionLayer\FieldCollection;
class EmizentechEntityDefinition extends EntityDefinition
{
    public const ENTITY_NAME = 'Emizentech_table';

    public function getEntityName(): string
    {
        return self::ENTITY_NAME;
    }

    public function getCollectionClass(): string
    {
        return EmizentechEntityCollection::class;
    }

    public function getEntityClass(): string
    {
        return EmizentechEntity::class;
    }

    protected function defineFields(): FieldCollection
    {
        return new FieldCollection([
            (new IdField('id', 'id'))->addFlags(new PrimaryKey(), new Required()),
            new StringField('name', 'name'),
            new CustomFields('custom_fields', 'customFields'),
            new CreatedAtField(),
            new UpdatedAtField()
        ]);
    }
}

Create An Entity EmizentechEntity.php:

Entity contains the setters and getters for its fields.

<?php declare(strict_types=1);
namespace EmizentechPlugin\Core\Content\EmizentechEntity;
use Shopware\Core\Framework\DataAbstractionLayer\Entity;
use Shopware\Core\Framework\DataAbstractionLayer\EntityIdTrait;
class EmizentechEntity extends Entity
{
    use EntityIdTrait;
    /**
     * @var string
     */
    protected $name;
    /**
     * @var array|null
     */
    protected $customFields;
    public function getName(): string
    {
        return $this->name;
    }
    public function setName(string $name): void
    {
        $this->name = $name;
    }
    public function getCustomFields(): ?array
    {
        return $this->customFields;
    }
    public function setCustomFields(?array $customFields): void
    {
        $this->customFields = $customFields;
    }
}

Create an Entity collection file name called EmizentechEntityCollection.php:

Entity collection is the most simple of the three but, is the most important one.

<?php declare(strict_types=1);
namespace EmizentechPlugin\Core\Content\EmizentechEntity;
use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
/**
 * @method void              add(EmizentechEntity $entity)
 * @method void              set(string $key, EmizentechEntity $entity)
 * @method EmizentechEntity[]    getIterator()
 * @method EmizentechEntity[]    getElements()
 * @method EmizentechEntity|null get(string $key)
 * @method EmizentechEntity|null first()
 * @method EmizentechEntity|null last()
 */
class EmizentechEntityCollection extends EntityCollection
{
    protected function getExpectedClass(): string
    {
        return EmizentechEntity::class;
    }
}

Entity Registration:

The last thing we need to do if we want our new entity to work, is to register it in the services.xml file:

<service id="EmizentechPlugin\Core\Content\EmizentechEntity\EmizentechEntityDefinition">
    <tag name="shopware.entity.definition" entity="Emizentech_table" />
</service>

We hope this post helped you learn how to create a Shopware plugin. We at Emizentech can also help you in building a custom Shopware plugin as we are a leading Shopware development company offering robust ecommerce solutions. So, whenever you need an expert Shopware developer please get in touch with us.
Source: https://shopwarian.com/shopware-6-plugin-programming-tutorial/.

Author

Founder and tech lead at Emizentech, Mr. Vivek has over ten years of experience in developing IT infrastructures and solutions. With his profound knowledge in eCommerce technologies like Shopware, Magento, and Shopify, Mr. Vivek has been assisting SMEs to enterprises across the globe by developing and maintaining their eCommerce applications. Technology innovation and trends insight come easy to Vivek with his thorough knowledge in the eCommerce domain. See him talking about ideas, trends, and technology in this blog. To know more about how Team Vivek can assist you in your eCommerce strategy? Connect team Vivek here.