The basics of Shopware 6

Shopware 6 plugin programming tutorial

In this article, we will create a sample plugin for Shopware 6, that will serve as a groundwork for us to use, whenever we need to create a new plugin. First, we will create a minimal plugin. Then we will add some commonly used structures like a subscriber, command, controller etc.. The resulting “skeleton plugin” will be available for you to download. You can just copy it, do some renaming, delete the structures, that you will not need and bang – you are ready to create some of the best Shopware 6 plugins out there!

Minimal Shopware 6 plugin setup

The absolute bare minimum for a functional plugin can be obtained by using the following command:

Note: If you don’t know, how to use the Shopware 6 Console and its commands, I suggest you go read this Console tutorial first.

This command will ask you for the name of the plugin. Let us name it SkeletonPlugin to highlight its purpose. Alternatively, you could also put the name of the plugin to the command as a parameter like this:

The command will then create a directory, named SkeletonPlugin, in your custom/plugins directory. It will also generate a subdirectory structure, that contains the composer.json, SkeletonPlugin.php and services.xml file.

I suggest we change the name of the plugin in composer.json file, so that it is unique. The generated default name is “swag/plugin-skeleton”, but we will change it to “shopwarian/plugin-skeleton” in my case or “you/plugin-skeleton” in your case. This way, it will be compliant with the standard, that I have arrogantly established in my plugin development guidelines article. ๐Ÿ™‚

If you go to the Administration now, you can see the plugin in the Extensions – My Extensions listing:

Note: In Shopware version 6.3. and lower, the plugin listing is under Settings – System – Plugins – My Plugins:

It does nothing, but it works! ๐Ÿ˜€

Adding the config settings for the Shopware 6 plugin

The topic of plugin configs is covered quite thoroughly in an article, named “How to create config for your Shopware 6 plugin“. So if you are new to this, you should probably read it later on, but for now, we will just create a minimal config file, which looks like this:

It should reside in the src/Resources/config directory of the plugin. Alternatively to creating the config.xml file manually, you can also create it automatically, when you call the plugin:create command with the option -c:

Adding a service to the Shopware 6 plugin

What do I mean by “service” here? Well, essentially just a PHP class, that provides some functionality for other parts of our application. For example you could have a ProductService, CustomerService etc., each handling a certain area of your app. Such a service usually contains methods, that are called by controllers or even other services. In order to be able to call them, the other classes must “inject” our service in their constructors. And for that, our service must be registered in Shopware, using the services.xml file.

So let’s create a skeleton of a service class. We will call it simply SkeletonService for now and we will also put it to the Service subdirectory. In real life, I recommend naming the service classes based on their topic or area plus the keyword Service. It is much easier to orient yourself, when you have your classes named properly, like ImportService, ExportService and so on. So this is how the skeleton service setup looks like in src/Service/SkeletonService.php:

And this is the updated version of services.xml file with the entry, containing the new service:

Adding an event subscriber to the Shopware 6 plugin

Event subscribers are one of the most useful features in Shopware 6. They allow you to trigger your code, if a specific event happens. In order to to create a subscriber, we need to create a PHP class, that implements EventSubscriberInterface and then register this class to the services.xml file and tag it as a subscriber. A standard name and location for the new PHP class would be src/Subscriber/Subscriber.php.

This is a basic event subscriber for Shopware 6, that contains some frequently used event handlers for the product detail page and checkout:

This is the entry in the services.xml file, containing the new subscriber:

Adding a command to the Shopware 6 plugin

Symfony console, that is used in Shopware 6, allows us not just to run some very useful commands, but also 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. The second method is, and in fact, must be named “execute”, because it gets automatically called, when you run the command from the console.

Obviously, in this method should be the main logic of your command, however I suggest keeping a bare minimum here, like for example argument validation, and calling a service from here instead. Why? To keep your application’s logic independent of from where or how is it called. The command itself should be just an entry, from which your main logic gets instructions and data, but the operations should be kept separately. There are cases, when you may want to have more ways to perform the same operation. For example you want to have a product import, that runs regularly as a command, using cron. But you also want to let the shop owner, who does not have access to the command line, to call it manually from the backend. If you put your main logic to both the command and backend, you will duplicate code unnecessarily and will run into problems rather soon. If you put it into a separate service and call this service from the command and the backend, then you will be able to maintain it easily, because your changes will have to be made in one place only.

We will name our command SkeletonCommand and put it into src/Command directory. In real life, I suggest naming the command classes, based on their purpose plus the keyword “Command”. For example ImportCommand, OldOrderArchivationCommand etc., so that it is apparent at first glance, what is the command’s main job.

Here comes the class SkeletonCommand.php:

Of course, we also need to add an entry to the services.xml file and tag it as a command to let Shopware know, that the “execute” method of our class should be called, when the specified command is executed in the console.

And this is how the command is executed and how its output looks like:

Adding a controller to the Shopware 6 plugin

Generally speaking, 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. As the names suggest, the former is used in the publicly visible part of the store, while the latter is used in the administration. For the purposes of our base plugin, we will add a storefront controller. The backend controller can be derived from it relatively easily, if needed.

First of all, we will add the storefront controller itself:

It resides in the src/Storefront/Controller directory and is called SkeletonController. 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 /skeleton is loaded in the user’s browser. For this to work, we need to add the /src/Resources/config/routes.xml file to our plugin’s structure:

It tells the system, that it should check the annotations (the commented code above the method declaration) and call the appropriate code, if a matching controller and method are found, when the user loads a page on our store. You can change the rules for the controller name matching later on, but an established standard is, that controllers are named like ‘SomethingController.php’.

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:

One last thing remains to be added and we are ready to test the controller on the URL address /skeleton on our Shopware 6 store. One of the most common tasks for storefront controllers to do, is to prepare a page to be displayed to the customer. 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/skeleton directory:

This template extends the base Shopware 6 page template, overriding its main base_content block. The output on your store’s page could look like this:

The sample template contains just the Twig’s dump command. It is extremely useful, because it displays all the variables, available in the template. As you can see, there is also a ‘customParameter’ present to demonstrate the way, how you can pass variables from the controller to the template. You will find the very same ‘customParameter’ in the SkeletonController.php. Please note, that you also have the Request and the Sales Channel Context at your disposal there. Imagine, what magic you could do with them.. ๐Ÿ˜‰

That is it! Our plugin now contains the basic mechanisms for handling the requests from our store’s frontend. You can find more on the topic of controllers in the official Shopware 6 documentation. You might also want to check the articles on how to send parameters to the controllers and how to access parameters in the controllers in Shopware 6 here on my blog.

Adding a migration to the Shopware 6 plugin

In the terminology of Shopware 6, a migration is a class, that manages the changes in the database schema. To put it more simply – it automatically runs the SQL commands, that change the structure of the database tables.

For the purposes of our groundwork plugin, we will create a migration, that will create a simple table with an ID, name and a custom fields column. By now, you have probably guessed the name of the new table.. Yes, it is ‘skeleton_table’. ๐Ÿ™‚

This is the raw SQL code, that we would manually use to create such a table:

However, we want our plugin to be portable and therefore we will add this code to the migration and make it a part of our plugin. A migration is a class, that we can create manually as any other, but it is easier to use a command, that is made specifically for this purpose. We just need to specify the name of the plugin, to which we want the migration be generated by Shopware and the name, that we want to append to the new migration (mainly for better readability purposes). The command looks like this:

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

All we need to do now, is to add the code to the update method of this migration class, that will execute the SQL command for creating the skeleton_table. The final migration therefore looks like this:

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

Adding an entity to the Shopware 6 plugin

So now, that we have taken care of the database table creation, we want to be able to work with this table via the DAL (data abstraction layer). This means, that we will be able to use a repository for our table, which is a standard, very convenient way to work with data in Shopware 6. In order to achieve this, we will have to define an entity.

In Shopware 6, we need three classes for creating a new entity over a database table: entity definition, entity class itself and entity collection.

Entity definition

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 using the table as a repository. In this class, you also define the fields for Shopware, based on your table columns.


Entity contains the setters and getters for its fields.

Entity collection

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

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:

Shopware 6 plugin skeleton download

So, the new groundwork plugin is finished. I hope it will serve you well, save you some time, if you are an experienced Shopware 6 developer and help you in the learning process, if you are a Shopware 6 beginner. Not every feature was thoroughly described, but I think it is an excellent playground for experimenting and learning the programming in Shopware 6.

Copying or even writing the code samples above by yourself could of course be helpful for learning the structure of Shopware 6 plugins. However, the more convenient way to go is to just download the ZIP archive, containing the plugin structure and files, unzip it to your custom/plugins directory and start editing.

Here is the link for the Shopware 6 base plugin download (ZIP archive):

Note: Do not forget to delete the files, that you do not need for your plugin and to remove the unnecessary entries from the services.xml file. Because nearly everything is named ‘skeletonSomething’, you can use mass replace text in your favorite editor quite conveniently, but please be aware of the case sensitivity – some expressions start with a lower case character, some with upper case.

If you have any suggestions on how to improve the skeleton plugin or what other commonly used features it should contain, feel free to leave a comment below. In any case, have fun with Shopware 6 plugin creation! ๐Ÿ™‚