Sylius plugins: How to get to the starting blocks

”A good developer is lazy”

When we want to add new features to Sylius it is rarely a unique set of needs we want to address. Sure, the particular project will have its unique set of demands and we will address them with different solutions, but these solutions will have been used in other settings before. This is why we have reusable libraries and frameworks such as Symfony and Sylius. 

When we face a particular problem in Symfony, re-usable code can be built as ”bundles”. Allowing us to reuse our solution within the context of other Symfony projects. When we face a particular problem in Sylius we write a variant of a Symfony bundle called a ”Sylius plugin”.

Being able to reuse or make public our solution will benefit many developers. Our future selves won’t have to build a solution from scratch anymore, which is how lazily good it is to build a Sylius plugin.

Building a Sylius plugin is not as easy as plug-and-play

It’s generally a good idea to start off with the official documentation on building a plugin when you want to extend Sylius. This article will highlight a few pain points to consider when we build a Sylius plugin, as this will complement the official documentation. These pain points are most readily addressed by following these recommendations:

  1. Follow the documentation.
  2. Symfony assumptions are being made, take heed.
  3. Gettings started is the hardest part.

A problem - a solution - a plugin

Recently I found myself in need of adding a payment method to Sylius. In my particular case, it was a Klarna Checkout payment method that I needed. No existing solution addressed my need. As Klarna is a popular payment provider in Sweden (and Nordic countries), it does not have as a wide spread customer base as Stripe or PayPal, but it serves millions of customers each year. Building a solution (a payment method for Klarna Checkout) would probably be re-used by third parties or in other projects where I will build an e-commerce solution for the Nordics. This is a pretty clear-cut case of where a Sylius plugin would be a suitable recourse.

We will set up the skeleton of a Klarna Checkout plugin! Note that this will not be an officially supported plugin or show up in the Sylius plugin store - these require Behat tests which we won’t be delving into in this article.

Getting to the starting block

We will start by creating a new project with the help of Composer and sylius/plugin-skeleton:

composer create-project sylius/plugin-skeleton VendorNameSyliusKlarnaGatewayPlugin

Note that it’s important to follow the naming convention. Symfony requires a "Bundle" postfix to load associated services. Sylius stipulates a vendor-name prefix and "Plugin" postfix. In my case, I would name it AndersBjorklandSyliusKlarnaGatewayPlugin.

This will make use of a skeleton project, packed with a full Sylius project to test your plugin against. It has an example plugin from the get-go which we will have to clear out if we do not want to browse it first. As Sylius requires BDD-tests for plugins to appear in the Sylius Plugin Store we will also see that it comes packed with a bunch of Behat tests to go along with the example plugin. So remember, clearing out the example plugin you should also clear out the tests too.

The skeleton plugin has some boilerplate to support its example plugin. You will want to make changes in composer.json and a bunch of files in both your project and in test/Application. The whole list of required changes to setup you up is listed at Sylius Plugins - Naming changes.


Set up name and namespaces

In composer.json:

"name": ”anders-bjorkland/sylius-klarna-gateway-plugin"

"autoload": {
    "psr-4": {
        "AndersBjorkland\\SyliusKlarnaGatewayPlugin\\": "src/",
        "Tests\\AndersBjorkland\\SyliusKlarnaGatewayPlugin\\": "tests/"
    }
},

Rename files

src/AcmeSyliusExamplePlugin.php -> src/AndersBjorklandSyliusKlarnaGatewayPlugin.php

src/DependencyInjection/AcmeSyliusExampleExtension.php -> src/DependencyInjection/AndersBjorklandSyliusKlarnaGatewayExtension.php


Adjust namespaces

The current namespaces will be Acme\SyliusExamplePlugin\…, you can search and replace or manually edit these. For me, this will become AndersBjorkland\SyliusKlarnaGatewayPlugin\… and I would change this in the files below.

  • src/AndersBjorklandSyliusKlarnaGatewayPlugin.php
  • src/DependencyInjection/AndersBjorklandSyliusKlarnaGatewayExtension.php
  • src/DependencyInjection/Configuration.php

Similarly will be done for the Sylius application residing under the tests folder:

tests/Application/Kernel.php has the namespace Tests\Acme\SyliusExamplePlugin\Application, it would become Tests\AndersBjorkland\SyliusKlarnaGatewayPlugin\Application.

Do not adjust the namespace for src/Kernel.php. It is set to work under the App namespace and is not required to be configured to work with the plugin. The same goes for public/index.php.

Adjust use statements

When we change namespaces we will also have to update how the classes are loaded in different files. So update the following files with the correct use statements.

  • tests/Application/public/index.php:
    Tests\Acme\SyliusExamplePlugin\Application\Kernel -> Tests\AndersBjorkland\SyliusKlarnaGatewayPlugin\Application\Kernel

Add a plugin to load with the test-application

The test application will load bundles and Sylius plugins via the file tests/Application/config/bundles.php. The example plugin is present here, so change its name accordingly:

Acme\SyliusExamplePlugin\AcmeSyliusExamplePlugin::class -> AndersBjorkland\SyliusKlarnaGatewayPlugin\AndersBjorklandSyliusKlarnaGatewayPlugin::class

Modify services configuration

If you are someone that prefers YAML over XML you will want to modify the Extension-file that is tasked with loading service configuration. So instead of using XmlFileLoader which is the current default in the example-plugin, make use of YamlFileLoader instead. Where you want to store the services.yaml file is up to you, I prefer to have it at src/Resources/config/services.yaml. We will tell the loader to load the correct file:

src/DependencyInjection/AndersBjorklandSyliusKlarnaGatewayExtension.php:

public function load(array $configs, ContainerBuilder $container): void
{
    $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
    $loader->load('services.yaml');
}

And more configuration!

Almost done with setting up a starting point for building a new plugin. We need to add a configuration node for the plugin. We will do that by modifying src/DependencyInjection/Configuration.php:

Change acme_sylius_example_plugin to something akin to anders_bjorkland_sylius_klarna_gateway_plugin.

Update composer autoloading

Update Composer, so it will autoload all the files correctly:

composer dump-autoload

Install assets and get some services running

The plugin skeleton addresses how to get started. There is a walk-through for a docker setup as well as a non-docker setup. I usually run a database in a Docker container and the application off of a native system. Minding this, here’s how we can set up such a system.

  • Disable all services but the database in docker-compose.yaml.
    Adjust tests/Application/.env:
    DATABASE_URL=mysql://root:[email protected]:3306/sylius_%kernel.environment%?charset=utf8mb4
  • Start the Docker container from the project root with docker-compose up -d.
  • If dependencies have not been installed, from project-root, run composer install.
  • Change directory to tests/Application.
  • Install assets and UI component. In tests/Application:
    Run
    yarn install
    Run
    yarn build
    Run
    bin/console assets:install public
  • Set up database and load fixtures. In tests/Application:
    Run
    bin/console doctrine:database:create
    Run bin/console doctrine:migrations:migrate
    Run bin/console sylius:fixtures:load
  • Start the web server. I prefer Symfony’s local web server. In tests/Application run symfony serve -d, alternatively php -S localhost:8000 public/index.php
  • You can now view the Sylius test application at localhost:8000/en_US and localhost:8000/admin

    Login credentials loaded with the fixtures:
    User:         sylius
    Password:     sylius

It’s a long way to set up a development environment for Sylius plugins, and it is important to follow the instructions carefully. But doing it this way means we will have reusable components that we can use repeatedly, and more people can benefit from our work.

I hope your development goes well and I’m looking forward to seeing your work in the Sylius eco-system.


Hero image by Hanna Auramenka.

Vad kan vi göra för dig?

Har du en bra idé eller ett problem som du behöver hjälp att lösa?
Ta gärna kontakt med oss för att se vad vi kan göra för er, och hur vi kan hjälpa er!

Contact us