How to add a custom translatable model?¶
In this guide we will create a new translatable model in our system, which is quite similar to adding a simple model, although it requires some additional steps.
As an example we will take a translatable Supplier entity, which may be really useful for shop maintenance.
1. Define your needs¶
A Supplier needs three essential fields: name
, description
and enabled
flag.
The name and description fields need to be translatable.
2. Generate the SupplierTranslation entity¶
Symfony, the framework Sylius uses, provides the Symfony MakerBundle, that simplifies the process of adding a model.
Warning
Remember to have the MakerBundle
imported in the AppKernel, as it is not there by default.
You need to use such a command in your project directory.
php bin/console make:entity
The generator will ask you for the entity name and fields. See how it should look like to match our assumptions.

As you can see we have provided only the desired translatable fields.
Below the final SupplierTranslation
class is presented, it implements the ResourceInterface
.
<?php
namespace App\Entity;
use Sylius\Resource\Model\AbstractTranslation;
use Sylius\Resource\Model\ResourceInterface;
class SupplierTranslation extends AbstractTranslation implements ResourceInterface
{
/**
* @var int
*/
private $id;
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $description;
/**
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $description
*/
public function setDescription($description)
{
$this->description = $description;
}
/**
* @return string
*/
public function getDescription()
{
return $this->description;
}
}
3. Generate the Supplier entity¶
While generating the entity, similarly to the way the translation was generated, we are providing only non-translatable fields.
In our case only the enabled
field.

Having the stubs generated, we need to extend our class with a connection to SupplierTranslation.
implement the
ResourceInterface
,implement the
TranslatableInterface
,use the
TranslatableTrait
,initialize the translations collection in the constructor,
add the
createTranslation()
method,implement getters and setters for the properties that are held on the translation model.
As a result you should get such a Supplier
class:
<?php
namespace App\Entity;
use Sylius\Resource\Model\ResourceInterface;
use Sylius\Resource\Model\TranslatableInterface;
use Sylius\Resource\Model\TranslatableTrait;
class Supplier implements ResourceInterface, TranslatableInterface
{
use TranslatableTrait {
__construct as private initializeTranslationsCollection;
}
public function __construct()
{
$this->initializeTranslationsCollection();
}
/**
* @var int
*/
private $id;
/**
* @var bool
*/
private $enabled;
/**
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @param string $name
*/
public function setName($name)
{
$this->getTranslation()->setName($name);
}
/**
* @return string
*/
public function getName()
{
return $this->getTranslation()->getName();
}
/**
* @param string $description
*/
public function setDescription($description)
{
$this->getTranslation()->setDescription($description);
}
/**
* @return string
*/
public function getDescription()
{
return $this->getTranslation()->getDescription();
}
/**
* @param boolean $enabled
*/
public function setEnabled($enabled)
{
$this->enabled = $enabled;
}
/**
* @return bool
*/
public function getEnabled()
{
return $this->enabled;
}
/**
* {@inheritdoc}
*/
protected function createTranslation()
{
return new SupplierTranslation();
}
}
4. Register your entity together with translation as a Sylius resource¶
If you don’t have it yet, create a file config/packages/sylius_resource.yaml
.
# config/packages/sylius_resource.yaml
sylius_resource:
resources:
app.supplier:
driver: doctrine/orm # You can use also different driver here
classes:
model: App\Entity\Supplier
translation:
classes:
model: App\Entity\SupplierTranslation
To check if the process was run correctly run such a command:
php bin/console debug:container | grep supplier
The output should be:

5. Update the database using migrations¶
Assuming that your database was up-to-date before adding the new entity, run:
php bin/console doctrine:migrations:diff
This will generate a new migration file which adds the Supplier entity to your database. Then update the database using the generated migration:
php bin/console doctrine:migrations:migrate
6. Prepare new forms for your entity, that will be aware of its translation¶
You will need both SupplierType
and SupplierTranslationType
.
Let’s start with the translation type, as it will be included into the entity type.
<?php
namespace App\Form\Type;
use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
class SupplierTranslationType extends AbstractResourceType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class)
->add('description', TextareaType::class, [
'required' => false,
])
;
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'app_supplier_translation';
}
}
On the SupplierTranslationType
we need to define only the translatable fields.
Then let’s prepare the entity type, that will include the translation type.
<?php
namespace App\Form\Type;
use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
use Sylius\Bundle\ResourceBundle\Form\Type\ResourceTranslationsType;
use Sylius\Resource\Translation\Provider\TranslationLocaleProviderInterface;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
class SupplierType extends AbstractResourceType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('translations', ResourceTranslationsType::class, [
'entry_type' => SupplierTranslationType::class,
])
->add('enabled', CheckboxType::class, [
'required' => false,
])
;
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'app_supplier';
}
}
7. Register the new forms as services¶
Before the newly created forms will be ready to use them, they need to be registered as services:
# config/services.yaml
services:
app.supplier.form.type:
class: App\Form\Type\SupplierType
tags:
- { name: form.type }
arguments: ['%app.model.supplier.class%', ['sylius']]
app.supplier_translation.form.type:
class: App\Form\Type\SupplierTranslationType
tags:
- { name: form.type }
arguments: ['%app.model.supplier_translation.class%', ['sylius']]
8. Register the forms as resource forms of the Supplier entity¶
Extend the resource configuration of the app.supplier
with forms:
# config/resources.yaml
sylius_resource:
resources:
app.supplier:
driver: doctrine/orm # You can use also different driver here
classes:
model: App\Entity\Supplier
form: App\Form\Type\SupplierType
translation:
classes:
model: App\Entity\SupplierTranslation
form: App\Form\Type\SupplierTranslationType
9. Define grid structure for the new entity¶
To have templates for your Entity administration out of the box you can use Grids. Here you can see how to configure a grid for the Supplier entity.
# config/packages/_sylius.yaml
sylius_grid:
grids:
app_admin_supplier:
driver:
name: doctrine/orm
options:
class: App\Entity\Supplier
fields:
name:
type: string
label: sylius.ui.name
sortable: translation.name
enabled:
type: twig
label: sylius.ui.enabled
options:
template: "@SyliusUi/Grid/Field/enabled.html.twig"
actions:
main:
create:
type: create
item:
update:
type: update
delete:
type: delete
10. Create template¶
# App/Resources/views/Supplier/_form.html.twig
{% from '@SyliusAdmin/Macro/translationForm.html.twig' import translationForm %}
{{ form_errors(form) }}
{{ translationForm(form.translations) }}
{{ form_row(form.enabled) }}
11. Define routing for entity administration¶
Having a grid prepared we can configure routing for the entity administration:
# config/routes.yaml
app_admin_supplier:
resource: |
alias: app.supplier
section: admin
templates: "@SyliusAdmin\\Crud"
redirect: update
grid: app_admin_supplier
vars:
all:
subheader: app.ui.supplier
templates:
form: App:Supplier:_form.html.twig
index:
icon: 'file image outline'
type: sylius.resource
prefix: admin
13. Check the admin panel for your changes¶
Tip
To see what you can do with your new entity access the http://localhost:8000/admin/suppliers/
url.