Diese Seite ist auch auf Deutsch verfügbar. Zur deutschen Seite wechseln

An introduction into Shopware 6 for Magento developers

An introduction into Shopware 6 for Magento developers

With the End Of Life of Magento 1 approaching fast, you're most likely already looking for a successor for building your next eCommerce project. Or after dipping your toes in Magento 2 you decided you are open to using a less complex solution.

For both cases, Shopware 6 can offer a stable and robust solution. Built to offer On-Premise solutions for smaller and mid-size webshops, as well as Enterprise platforms, Shopware 6 prides itself on simplicity and ease of use for developers.

Let's do an in-depth dive into Shopware 6 from the perspective of a Magento developer and see why it might offer a good fit for your next eCommerce project.

Shopware 6 architecture

At the basis, Shopware 6 is built around Symfony. Staying as close to the principles of this popular framework as possible. The database is built on MySQL 5.7.

For the frontend, the Twig templating engine is used, with Bootstrap as a CSS framework and jQuery for javascript. In the admin area, VueJS creates easy to use forms and grids.

This means the stack is relatively basic, and if you don't already know these technologies, they should be reasonably easy to learn because of a low learning curve and large communities around them. There are plenty of tutorials, youtube videos, and StackOverflow posts out there to dive right in and solve any challenge you might encounter. 

Shopware 6 is divided into roughly three areas.

The frontend, or as Shopware calls them: "Sales Channels", cover anything from the HTML frontend, to publicly exposed APIs to use for a headless approach, or integrations into Amazon or Instagram. On a traditional frontend, a Sales Channel corresponds more or less with a Magento Store View, and Website scope, rolled into one. Language, currency, payment methods, and specific catalog selections are all set per Sales Channel.

The admin area is where catalog, sales, and customer management is performed, as well as the module, or "Plugin" management, and configuration settings on both the global level as well as on the various Sales Channels.

The last area is present in code only; the Shopware Core. This part powers all other areas and does not offer any graphical interface. Instead it exposes a large set of API endpoints that allows developers to tap into any feature Shopware offers.

Getting started on code

To better understand how Shopware 6 works for developers, we're going to walk through the setup of your development environment, and the creation of a plugin.

There are many ways to develop on Shopware 6, depending on your personal preference. From XAMPP and an FTP connection to using GIT and CI/CD pipelines, it's all possible.

For today, the development environment setup will use Docker and GIT. If you prefer another setup, feel free to do so and skip ahead to the Writing A Plugin chapter.

Setting up the local Development environment

For our local development environment, we'll use a local Docker setup provided by community member Shyim. His Shopware Docker repository, or SWDC for short, offers an easy way to run both Shopware 6, and 5, via an optimized Docker setup. It also provides a powerful command-line tool to quickly provision your installation, clear cache, and create Plugin- and theme skeletons very similar to what Magerun offers for Magento.

Before we get started, make sure you are running the latest version of Docker, and Docker Compose.

The first step is to clone the Shopware Docker repository onto your local system. Any location will do like, for example, your home directory.

Next, for Linux and Mac, you might want to add a symlink to the directories swdc file in /usr/local/bin. This makes it easy to call the tool while developing.
SWDC comes with a list of configurable options that can be set by a .env file.
Copy .env.dist to $HOME/.swdc_env and configure it to your preference. The one setting that is required is "CODE_DIRECTORY", point this to the directory your projects are developed in.

Having SWDC installed we can start with the project itself. Again, there is no one way to set up the actual project, but the following setup does work well for multiple projects. 

We'll start by cloning https://github.com/shopware/development, to our previously defined "CODE_DIRECTORY", into a specific project directory name.

$ git clone git://github.com/shopware/development yourProjectName

There are two Shopware 6 boilerplate repositories; shopware/development and shopware/production.
The two boilerplates are almost identical. The difference can be found in the end goal for which you develop. "Development" can be used for working on the Shopware Core, or developing a single plugin, like in this article, while "Production" is to be used for projects.

Now we clone the Shopware core into the project.
$ git clone git://github.com/shopware/platform platform

This pulls in the Shopware 6 core.

For projects, it is recommended to fork shopware/production as your project starting point and use the composer.json for including 3rd party, as well as your own plugins.

The purpose of our plugin will be to add physical store address fields to a Sales Channels' admin view, which we can display in the fronted in case the customer wants to visit our brick and mortar store.

Provisioning your Shopware Installation

On the command line, make sure you are in the directory of your project and run $ swdc up. This will spin up the Docker containers for your project. Not just the application server (via http://{yourdirectory}.dev.localhost) but a PHPMyAdmin (via http://db.localhost), and a mail catcher (via http://mail.localhost), as well as an Elasticsearch container.

Now we want to run $ swdc build{yourdirectory}. This will install the database and run any other commands to prepare the installation, including sample data for your Shopware 6 project.

Once that is done you can view your project via http://{yourdirectory}.dev.localhost in the browser, or the Docker command line with $ swdc shell.

Configuring tools

To make your life easier, there are some tools to recommend for development.
For those working with PHPStorm make sure to install the Symfony Support plugin and the Twig plugin. This will offer some powerful features such as code completion, service config generation or service id inference.

In general, since Shopware 6 is based on standard frameworks such as Symfony, Doctrine, and Twig, the tools around those frameworks are also supported.

A second tool you can't miss is the DevelopmentHelper from Friends Of Shopware. This plugin adds some handy features such as showing the available Twig variables in the Symfony Debug Toolbar included in Shopware 6, as well as the Template Hints you might be used to working with, in Magento, and many more features.
Installing is easy enough. Git Clone into your projects custom/plugins and you are good to go. Make sure to require nikic/php-parser and friendsofphp/php-cs-fixer though composer as they are needed by the plugin.

Friends Of Shopware, also known as Frosh, is a community of Shopware developers that publish tools to the community. If you're interested in handy tools for Shopware be sure to follow them on Twitter and Github.

Writing a plugin

Now that our development environment is set up, we can proceed with creating new functionalities and code for our Shopware 6 project. There are various ways to go about this but for the purpose of this article we will be doing all modifications in plugins in the custom/plugins directory.


Plugin Scaffold

First, we'll enter the Docker container for our application. On the command line use $ swdc shell to go to the Docker container command line.
We enter the directory specified in SWDC configuration under "CODE_DIRECTORY", so we'll have to enter our specific project directory: $ cd {yourdirectory}.

Shopware 6 comes packed with a powerful command line tool we can access through $ bin/console. Much like you are used from Magerun or bin/magento you can clear cache, install plugins and create admin users for example. But it has some other powerful features as well

    • Dumping Shopware Admin configuration in a JSON file
    • Creating entities for the Database Abstraction Layer (DAL), much like Resource Models
    • Various debug information lists
    • Cache prewarming
    • Lint checkers for yaml, xml and twig to spot typos
    • Creating sales channels
    • Creating themes
    • Creating plugins

And of course many more options which I encourage you to via $ bin/console -h.

We'll go for the last one on the list, creating Plugins. It's worth mentioning that a Theme is a plugin itself. The biggest one being a theme.json added to the Plugin, under custom/plugins/{ThemeName}/src/Resources/, structure that defines properties such as the name, and how Theme inheritance is handled.

For now let's generate our Plugin using the command $ bin/console plugin:create StoreAddress.
This will create the path custom/plugins/StoreAddress which will be the root path of our plugin.


each plugin has a composer.json which holds some information for Shopware itself, under the key "extra", such as the name of the Plugin, as well as the namespace. Next to that this allows you to publish Plugins via a composer repository if you like.
StoreAddress.php this file will take on the name of your plugin and can be likened somewhat to Magento 2 registration.php.
Next to that it is an entrypoint for the plugin that offers events such as install, postInstall, activate and uninstall. By adding these methods to your file, you can execute code on these specific events.
Src This directory holds most of the files that make up the plugin, in subdirectories such as Services your PHP files will be added.
src/Resources The resources directory will hold static files and assets such as configuration XML, template files and javascript.
Services.xml handles various topics

    • Dependency Injection, which is offered through the standard Symfony container DI, and works roughly the same as Magento 2 "di.xml".
    • Declaring Event subscribers, which work exactly like Magento Observers.
    • Defining the services themselves. Services are classes that perform a certain action, often containing Business Logic.

Much like you are used with in Magento, Shopware uses XML for configuration around your plugin. Examples are the config.xml and services.xml.

Now that we have our basis, we can start to work on implementing functionalities. To begin with we'll need to add a few extra fields to our Store SalesChannel.

Database update and admin fields

Much like Magento Shopware 6 has a wide range of entities. Anything from Customers to Products, Categories and Orders. But Sales Channels are entities as well. All of these entities can be treated the same under the hood, and so adding new fields to them also works in a similar fashion.
Added fields are stored by Shopware 6 in the database table custom_field. A field set, which we'll also create is stored in custom_field_set.

We'll start with declaring our service, and adding some dependencies in the services.xml

Here we point the service to our yet to be created CustomFieldService class, which will create the new entity fields, and it adds two dependencies.

    • The DI container which gives us access to various declared services.
    • The Sales Channel Repository which gives us the Data Abstraction Layer, or DAL for short, class for the Sales Channels, similar to Resource classes in Magento.

With the services.xml entry we'll create our class next.

We start the file with <?php declare(strict_types=1);. Although not mandatory, and defined per file, it's good to remember throughout Shopware 6 Strict Typing is enforced, which means you will have to pass along the right type of variable as an argument to core classes.

In the constructor we receive our dependencies in services.xml, and add those to class variables.

Now we add a createCustomFields method that will add the actual field to the custom_field table.

The $context will be passed along when we call it, it's not directly used by us, but by underlying methods that need to be aware of it.

We will use the Sales Channel Entity repository, and its upsert method that will either insert the fields, or update if they already exist. Each entity field we insert will have a unique ID generated via the Uuid::randomHex method. It's important to add it at all when creating new entity fields.

We start with a new field set called Store Address, with an ID, a name, and labels that will be displayed in the admin.

Next we add a street, postal code and city field, feel free to add more fields of course. The fields get an ID, a name used in the database, and a type. There are several types available, which are best to be viewed in the class Shopware\Core\System\CustomField\CustomFieldTypes. Last, we can set various config values. In general each field will have a

    • componentName
    • customFieldType
    • customFieldPosition
    • Labels

As mentioned in the beginning the Shopware admin is based on VueJS. Under the hood this definition will create a VueJS component which will be displayed. In this case a "sw-field".
There isn't one definitive overview of customFieldTypes available. I recommend looking at similar methods, and see what values they use to get a sense of options.

Last, we indicate these are fields assigned to the Sales Channel, with a relationship declaration. We do this by retrieving the Sales Channel repository class, calling the method getEntityName. This way Shopware 6 knows the fields belong to Sales Channel entities.

We still need to make sure this method is executed whenever our Plugin is activated, or installed. Earlier I mentioned the file StoreAddress.php offers such options. So let's head over there and add an install event.

We'll need to add a standard constructor that gives us the container, and calls the parent constructor.
Next we add an install method which will call our newly created custom field class to create the fields.

As you can see it is little more than instantiating the class, passing the right dependencies and calling out createCustomFields method.

For the dependency we do not declare a class directly, but use the Symfony Container get method to reference it. The custom_field_set.repository has been declared in the core of Shopware 6, and will automatically resolve to the correct class. Please note, this is a generated service id, based on the custom_field_set registered as an entity and a runtime-generated EntityRepository, which got pseudo-typed on custom_field_set. Looking for 'custom_field_set.repository' will give you no corresponding config file.

Let's install our plugin, and get those fields added. Open up your Docker commandline and and run the command $ bin/console plugins:refresh.
This command makes Shopware search for all plugins, which we can then list using $ bin/console plugins:list.



I've previously installed the DevelopmentHelper, but the StoreAddress plugin is not installed or active yet, we can do so with the command $ bin/console plugins:install --activate -c StoreAddress.
This installs the StoreAddress plugin, activates it and clears the cache all at the same time.

Now that this is done check the PHPMyAdmin via http://db.localhost, in your projects database, table custom_field, will show 3 new fields "storefront_zipcode", "storefront_city" and "storefront_street".

And without any more effort we have the fields in the Admin as well. Open up http://{yourproject}.dev.localhost/admin and log in with username "admin", password "shopware". Scroll down to Sales Channels, and click on "Storefront".
At the bottom of the page you will find the custom fields which can be filled in for our next step.


Adding data in the frontend

We can now display the new fields in the frontend. For this exercise we will add them to the footer on each page.

For that purpose we will use the theme overwriting mechanism that Shopware 6 offers, which is somewhat similar to what you are used to in Magento. 

In our plugin we create the file src/Resources/views/storefront/layout/footer/footer.html.twig. This file also exists in the base theme of Shopware 6 and we'll simply overwrite it and add our information.

One of the powerful functionalities we get from Twig, the Shopware 6 template engine, is the ability to extend the parent. This means we are not required to have the entire footer in our file, just the block we wish to rewrite.

Since our data is stored in the Sales Channel we do not have to put in too much effort to retrieve the data on the frontend. The context variable, that stores all information about the sales channel, page, and much more, is already globally available. 
We can take a look at it through the Symfony Debug Toolbar, using the addon of the Development Helper plugin we installed, and activated at the start.

Open your shop in the browser, and right click on the Twig tab in the Debug Toolbar


This will open up a new browser tab, displaying information on the Twig templates.The Development Helper plugin gives us at the top of the page also the variables available in the template. Expand context > salesChannel > customFields, and there you will see the address if you saved if before in the admin.

This path we can now use in our template.

We extend the original file, and add just our new column to the layout_footer_navigation_columns block. Twig is a powerful templating engine, and I encourage you to read more about it on their website.

Now we have our address neatly displayed in the footer.


Custom page controller

Let's take this one step further by adding a new Store Locator page, where we display all the stores. For this we'll add a new controller and a small template.

We start again in the services.xml, where we add the controller and make sure all dependencies are available.

Here we also specify the controller being publicly available. Next up we create a routes.xml alongside the service.xml where notify Shopware we have controllers that are available.

By using a greedy annotation (*) we let Shopware grab all PHP files ending with Controller.php, from the controller directory. And with that we create our controller file: src/Controller/StoresController.php.

We have a couple of things going on here that we will walk though. First off we have the annotation /** @RouteScope(scopes={"storefront"}) */ at the top of the class.

Since Shopware 6 relies on the routing offered by Symfony we can also use that way of declaring routes.
The scope indicates where this controller will be accessible. There are several scope available:

    • Administration
    • Storefront
    • SalesChannelApi
    • StoreApi
    • Route
    • ApiRoute

Next is the annotation /** @Route("/storelocator", name="frontend.store.locator.index", options={"seo"="true"}) */ found on the indexAction. "Action" is used to indicate this method is tied to an actual page. The annotation gives the route under which it will be available (http://{yourdirectory}.dev.localhost/storelocator) and the name of the controller.
Next to that we can define various options such as if the page is indexable by Search Engines. Here we set this to true.

If you open up the page you won't see a whole lot at this point though, we need to start adding some dates.

Our aim is to retrieve all Sales Channels, we can do this by performing a search on the Sales Channel entity repository we injected in services.xml.

The search works much like filtering a collection in Magento by adding various filters. You can read about the different filters in the Shopware developer Documentation.

This will give us back a collection, but without knowing the intimate details of Shopware 6 it's hard to know what we're getting exactly. For this we can use one of SWDCs' powerful tools: swdump.

First open a new terminal, and run $ swdc debug {yourdirectory}. This will open a Symfony Var Dumper Server view.

Next, in our code, we add

Now visit your storelocator page and watch the Symfony Var Dumper Server populate with data.


Swdump offers an easy way to explore data in a request, without printing it directly to the screen. In this case when we scroll down we will see that in Elements, there are two sales channels. The first for APIs, the second is our Store front. With this information we can return the right data to our template.

The return is a rendering of a file called storefront/store/index.html.twig. This template doesn't exist yet, so we'll go ahead and create it in our Plugin under the path src/Resources/views/storefront/store/index.html.twig.

Since we are not overwriting any existing templates, we can simply extend the Shopware base template, and add content to the block base_content, which holds the main content on any page.

If you are familiar with Twig you'll see we are looping through the stores array we passed from our controller, skipping and sales channels that do not have the city field filled in.
This will hold true, for example, for the API sales channel.
For each Sales Channel we then retrieve the name, street, postal code and city.
The end result is a list with addresses on a separate page.


What's next

I'm sure this short introduction got you as excited about Shopware 6 as I am. When you are ready for your next steps there is a world full of community AND documentation to discover.

Make sure to join the Shopware Gitter channel to connect with other developers. Also follow Shopware Dev on twitter, to keep up to date, and make sure to participate in the Shopware Community Day, organized each year.

For documentation your best place to start is docs.shopware.com which has both end users, as well as technical documentation. Especially useful are the HowTos which give you a ton of cookbook recipes on various topics.

We're looking forward to seeing your next project launch on Shopware 6!

This could be also interesting:

Shopware for Magento developers

Shopware 6.2 Release Candidate


Never miss out - get all the latest news sent straight to your inbox.

Shopware Logo
Your opinion matters to us!

Honest feedback is valuable and our basis for improvement. That's why we're asking you to take part in our latest survey on Shopware as a brand. It will probably take you less than 5 minutes.