Introduction
So you are a Symfony developer and you just stumbled upon Sylius — maybe you were browsing YouTube at 2 a.m., maybe you were procrastinating from the things you actually have to do. Either way, you are wondering: what the hell is this Sylius thing and how do I start with it?
In this episode we will install Sylius from scratch using Docker and then do our first real customization — adding a new field to a product. The goal is to show you that Sylius is approachable and that you can achieve something meaningful on your very first try.
Some background with Symfony and PHP is recommended, but not strictly required. The presenter started working with Sylius without any PHP or Symfony experience — and somehow made it a career.
Installing Sylius with Docker
To use Sylius you need to install it. We start by cloning the Sylius Standard repository — this is the official starter template that gives you a ready-to-go e-commerce application:
git clone https://github.com/Sylius/Sylius-Standard.git sylius-101
cd sylius-101
Once inside the project directory, one command does everything:
make init
This single command:
- Starts the Docker containers
- Pulls and builds the required images
- Installs Composer dependencies
- Installs front-end dependencies
- Creates the database
- Loads fixtures (sample data so you can actually browse products, place orders, etc.)
You do not need to understand everything that
make initdoes right now. At the very beginning, treat it as a black box. We will dive deeper into what happens under the hood in future episodes.
The presenter uses a Mac, but you can use Linux, Windows, or — as he puts it — even clay tablets. It is your life.
Exploring the Application
After installation finishes, open localhost in your
browser. You should see the Sylius storefront — a fully
functional e-commerce shop already populated with sample products.
Three tools are used throughout the tutorial:
- Browser — to see the storefront and admin panel
- PhpStorm (or any IDE) — to write code
- Terminal — to run commands inside Docker containers
Now navigate to the admin panel:
# Visit in your browser:
http://localhost/admin
The default credentials are:
- Username:
sylius - Password:
sylius
Security is very high, as you can tell. Inside the admin you will find products, customers, orders — all the building blocks of an e-commerce platform, already wired up and working.
The Task: Manufacturer SKU
Let's imagine a real scenario. You open your backlog and find this task: add a manufacturer SKU field to products.
If you go to Products in the admin panel and edit one, you will see a Code field. This is Sylius's internal identifier for the product within the application. But there is nothing that identifies the product outside the application — no manufacturer reference, no external SKU.
We want:
- A new
manufacturerSkufield on the Product entity - The field editable in the admin panel (next to the code field)
- The field visible on the shop product page
Let's make it happen.
Modifying the Product Entity
In Sylius Standard, the Product entity lives in
src/Entity/Product/Product.php. It extends the base Sylius
Product class, which means you can add your own properties on top of
everything Sylius already provides.
Add a new property with a nullable string type:
// src/Entity/Product/Product.php
class Product extends BaseProduct
{
private ?string $manufacturerSku = null;
public function getManufacturerSku(): ?string
{
return $this->manufacturerSku;
}
public function setManufacturerSku(?string $manufacturerSku): void
{
$this->manufacturerSku = $manufacturerSku;
}
}
The property is nullable because not every product will have a manufacturer SKU. Existing products in the database should not break when the new column appears.
If you are using PhpStorm, you can use the built-in generator (Code > Generate) to create getters and setters automatically. Don't waste time on the mechanical stuff.
ORM Mapping & Migration
The property exists in PHP, but the database does not know about it yet. We need to add Doctrine ORM mapping so the new field gets a corresponding column:
use Doctrine\ORM\Mapping as ORM;
#[ORM\Column(name: 'manufacturer_sku', type: 'string', nullable: true)]
private ?string $manufacturerSku = null;
Sylius Standard already has existing mapping that tells Doctrine this
entity maps to the sylius_product table — we are just adding
a new column to it.
Now jump into the PHP container and generate a migration:
docker exec -it sylius101-php bash
bin/console doctrine:migrations:diff
Doctrine generates a migration file with an
ALTER TABLE sylius_product ADD manufacturer_sku VARCHAR(255) DEFAULT NULL
statement. Run it:
bin/console doctrine:migrations:migrate -n
Tip: The
-nflag means "non-interactive" — it skips the "Are you sure?" confirmation prompt. Handy during development so you do not have to answer the same question every time.
The new column is now in the database. But it is not yet visible anywhere in the UI.
Form Type Extension
To make the field editable in the admin panel, we need a form type extension. This is a standard Symfony mechanism for adding fields to existing forms without modifying the original class.
// src/Form/Extension/ProductTypeExtension.php
namespace App\Form\Extension;
use Sylius\Bundle\AdminBundle\Form\Type\ProductType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
class ProductTypeExtension extends AbstractTypeExtension
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('manufacturerSku', TextType::class, [
'required' => false,
'label' => 'Manufacturer SKU',
]);
}
public static function getExtendableTypes(): iterable
{
return [ProductType::class];
}
}
Gotcha: Sylius has two ProductType classes — one in the Product Bundle and one in the Admin Bundle. Make sure you extend the one from
Sylius\Bundle\AdminBundle. The Admin Bundle's type is the one actually used in the admin panel forms.
After creating the extension, refresh the admin product edit page. Nothing changes yet — the form field is registered in the backend, but the template does not render it. We need to tell Sylius where to display the new field in the UI.
Twig Hooks
There are two ways to customize Sylius templates — or as the presenter's high school maths teacher put it, two ways to solve a problem:
- The tank way — override the entire template. Brutal, sometimes effective, but you end up maintaining a full copy of the original template and miss out on future Sylius updates.
- The sophisticated way — use Sylius Twig Hooks. This is a newer feature that lets you inject small templates between or after existing template blocks without touching the originals.
Twig Hooks work like extension points scattered throughout the Sylius templates. Each hook has a name and a priority that determines where your injected content appears relative to other blocks.
We will use the sophisticated way.
Admin UI Customization
First, we need to find the correct hook name. Open a product edit page in the admin and look at the Symfony debug toolbar at the bottom of the page. You will see a Twig Hooks icon — this is specific to Sylius and not part of standard Symfony.
Click it to see all the hooks rendered on the current page. Since we want the manufacturer SKU near the code field, look for a hook containing "code" in its name. You will find something like:
sylius_admin.product.update.content...code (priority: 500)
The code field renders at priority 500. If we inject our template at priority 550, it will appear right after the code field.
Create a simple Twig template for the field:
# templates/admin/product/_manufacturer_sku.html.twig
{{ form_row(hookable_metadata.context.form.manufacturerSku) }}
Then register it in your Twig Hooks configuration:
# config/packages/sylius_twig_hooks.yaml
sylius_twig_hooks:
hooks:
'sylius_admin.product.update.content.general.section.fields':
manufacturer_sku:
template: 'admin/product/_manufacturer_sku.html.twig'
priority: 550
Refresh the admin product page — the Manufacturer SKU field appears right after the code field. Fill it in, click Save, and the value persists. That's it for the admin panel.
Shop UI Customization
The same Twig Hooks approach works for the storefront. We want to display the manufacturer SKU on the product detail page, near the existing product code.
The process is identical:
- Find the relevant hook name using the debug toolbar on the shop product page
- Create a template that renders the manufacturer SKU value
- Register it in the Twig Hooks config with the appropriate priority
# templates/shop/product/_manufacturer_sku.html.twig
{% if product.manufacturerSku %}
<span>Manufacturer SKU: {{ product.manufacturerSku }}</span>
{% endif %}
After configuring the hook and refreshing the shop product page, the manufacturer SKU appears below the product code — visible to customers browsing the store.
The presenter intentionally made a few path mistakes while coding this part live. If something does not render, double-check your template paths in the hook configuration — typos are the most common cause.
Conclusion
In this episode we went from zero to a working Sylius customization:
- Installed Sylius with a single
make initcommand using Docker - Added a new entity property
(
manufacturerSku) to the Product - Created a Doctrine migration to add the column to the database
- Extended the admin form with a form type extension
- Used Twig Hooks to inject the field into both the admin panel and the shop storefront — without overriding any templates
The whole thing fits in about 15 minutes. If you are not 100% sure about what happened, watch it one more time. And if you have questions, leave a comment or reach out directly.
Presented by Mateusz from Commerce Weavers. If you need broader experience with Sylius or help with your Symfony e-commerce projects, all the links are in the video description.