Adding data translations

Overview

In this guide you'll learn how to add translations to entities.

Prerequisites

This guide is built upon the plugin base guide, but any plugin will work here. Just note that all examples are using the plugin mentioned above.
In order to create data translations you need an existing entity, as this guide is based on the Adding custom complex data guide, you should have a look at it first.
Here's a video dealing with data translations from our free online training "Backend Development".

Creating the migration

We'll start with creating a new database table. Make sure to use the name of your entity appending _translation.
In this guide we'll name our table swag_example_translation since our entity is named swag_example.
The translation table's columns should be the following:
`swag_example_id`This will refer to the `swag_example` entity this translation belongs to. This is also a foreign key.`language_id`This will contain the ID of the language for this translation. This is also a foreign key.`name`The actual translated value, the translated name of the `swag_example` entity.`created_at`Date when the translations has been created.`updated_at`Date when the translations has been updated.
This is how your migration could look like:
<plugin root>/src/Migration/Migration1612863838ExampleTranslation.php
1
<?php declare(strict_types=1);
2
3
namespace Swag\BasicExample\Migration;
4
5
use Doctrine\DBAL\Connection;
6
use Shopware\Core\Framework\Migration\MigrationStep;
7
8
class Migration1612863838ExampleTranslation extends MigrationStep
9
{
10
public function getCreationTimestamp(): int
11
{
12
return 1612863838;
13
}
14
15
public function update(Connection $connection): void
16
{
17
$query = <<<SQL
18
CREATE TABLE IF NOT EXISTS `swag_example_translation` (
19
`swag_example_id` BINARY(16) NOT NULL,
20
`language_id` BINARY(16) NOT NULL,
21
`name` VARCHAR(255),
22
`created_at` DATETIME(3) NOT NULL,
23
`updated_at` DATETIME(3) NULL,
24
PRIMARY KEY (`swag_example_id`, `language_id`),
25
CONSTRAINT `fk.swag_example_translation.swag_example_id` FOREIGN KEY (`swag_example_id`)
26
REFERENCES `swag_example` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
27
CONSTRAINT `fk.swag_example_translation.language_id` FOREIGN KEY (`language_id`)
28
REFERENCES `language` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
29
)
30
ENGINE = InnoDB
31
DEFAULT CHARSET = utf8mb4
32
COLLATE = utf8mb4_unicode_ci;
33
SQL;
34
$connection->executeStatement($query);
35
}
36
37
public function updateDestructive(Connection $connection): void
38
{
39
}
40
}
Copied!

Creating the translation entity

The translation is an aggregation to the ExampleEntity. Therefore, you should place it into the <plugin root>/src/Core/Content/Example/Aggregate directory. In this directory we create a subdirectory called ExampleTranslation where we create a new definition for our translation which is called ExampleTranslation.

EntityDefinition class

Now we can start creating our ExampleTranslationDefinition which extends from Shopware\Core\Framework\DataAbstractionLayer\EntityTranslationDefinition. Special for entity translation is, that we have to override a method called getParentDefinitionClass which returns the definition class of our entity we want to translate. In this case it's ExampleDefinition.
<plugin root>/src/Core/Content/Example/Aggregate/ExampleTranslation/ExampleTranslationDefinition.php
1
<?php declare(strict_types=1);
2
3
namespace Swag\BasicExample\Core\Content\Example\Aggregate\ExampleTranslation;
4
5
use Shopware\Core\Framework\DataAbstractionLayer\EntityTranslationDefinition;
6
use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\Required;
7
use Shopware\Core\Framework\DataAbstractionLayer\Field\StringField;
8
use Shopware\Core\Framework\DataAbstractionLayer\FieldCollection;
9
use Swag\BasicExample\Core\Content\Example\ExampleDefinition;
10
11
class ExampleTranslationDefinition extends EntityTranslationDefinition
12
{
13
public const ENTITY_NAME = 'swag_example_translation';
14
15
public function getEntityName(): string
16
{
17
return self::ENTITY_NAME;
18
}
19
20
public function getParentDefinitionClass(): string
21
{
22
return ExampleDefinition::class;
23
}
24
25
protected function defineFields(): FieldCollection
26
{
27
return new FieldCollection([
28
(new StringField('name', 'name'))->addFlags(new Required()),
29
]);
30
}
31
}
Copied!
As you can see, we've implemented a StringField for the name column, the other fields like the language_id will be automatically added by the EntityTranslationDefinition since they are base fields of it.
All that's left to do now, is to introduce your ExampleTranslationDefinition to Shopware by registering your class in your services.xml file and by using the shopware.entity.definition tag, because Shopware 6 is looking for definitions this way. Note, that we have to register the translation after the entity we want to translate.
Here's the services.xml as it should look like:
<plugin root>/src/Resources/config/services.xml
1
<?xml version="1.0" ?>
2
<container xmlns="http://symfony.com/schema/dic/services"
3
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
5
6
<services>
7
<service id="Swag\BasicExample\Core\Content\Example\ExampleDefinition">
8
<tag name="shopware.entity.definition" entity="swag_example" />
9
</service>
10
11
<service id="Swag\BasicExample\Core\Content\Example\Aggregate\ExampleTranslation\ExampleTranslationDefinition">
12
<tag name="shopware.entity.definition" entity="swag_example_translation" />
13
</service>
14
</services>
15
</container>
Copied!

Entity class

So far we introduced our definition, we can create our ExampleTranslationEntity. Our entity has to extend from the Shopware\Core\Framework\DataAbstractionLayer\TranslationEntity which comes with some getters and setters for the the language_id. We only have to add three properties here, one for the example_id, one for the actual name and one for the association to the ExampleEntity. All of those properties need a getter and a setter again, so add those too.
Here's our ExampleTranslationEntity:
<plugin root>/src/Core/Content/Example/Aggregate/ExampleTranslation/ExampleTranslationEntity.php
1
<?php declare(strict_types=1);
2
3
namespace Swag\BasicExample\Core\Content\Example\Aggregate\ExampleTranslation;
4
5
use Shopware\Core\Framework\DataAbstractionLayer\TranslationEntity;
6
use Swag\BasicExample\Core\Content\Example\ExampleEntity;
7
8
class ExampleTranslationEntity extends TranslationEntity
9
{
10
protected string $exampleId;
11
12
protected ?string $name;
13
14
protected ExampleEntity $example;
15
16
public function getExampleId(): string
17
{
18
return $this->exampleId;
19
}
20
21
public function setExampleId(string $exampleId): void
22
{
23
$this->exampleId = $exampleId;
24
}
25
26
public function getName(): ?string
27
{
28
return $this->name;
29
}
30
31
public function setName(string $name): void
32
{
33
$this->name = $name;
34
}
35
36
public function getExample(): ExampleEntity
37
{
38
return $this->example;
39
}
40
41
public function setExample(ExampleEntity $example): void
42
{
43
$this->example = $example;
44
}
45
}
Copied!
Now we need our translation definition to know its custom entity class. This is done by overriding the method getEntityClass in our ExampleTranslationDefinition.
<plugin root>/src/Core/Content/Example/Aggregate/ExampleTranslation/ExampleTranslationDefinition.php
1
class ExampleTranslationDefinition extends EntityTranslationDefinition
2
{
3
[...]
4
5
public function getEntityClass(): string
6
{
7
return ExampleTranslationEntity::class;
8
}
9
}
Copied!

EntityCollection

As we already know, we should create an EntityCollection for our Entity too. For entity translations it is the same way as for normal entities.
Our collection class could then look like this:
<plugin root>/src/Core/Content/Example/Aggregate/ExampleTranslation/ExampleTranslationCollection.php
1
<?php declare(strict_types=1);
2
3
namespace Swag\BasicExample\Core\Content\Example\Aggregate\ExampleTranslation;
4
5
use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
6
7
/**
8
* @method void add(ExampleTranslationEntity $entity)
9
* @method void set(string $key, ExampleTranslationEntity $entity)
10
* @method ExampleTranslationEntity[] getIterator()
11
* @method ExampleTranslationEntity[] getElements()
12
* @method ExampleTranslationEntity|null get(string $key)
13
* @method ExampleTranslationEntity|null first()
14
* @method ExampleTranslationEntity|null last()
15
*/
16
class ExampleTranslationCollection extends EntityCollection
17
{
18
protected function getExpectedClass(): string
19
{
20
return ExampleTranslationEntity::class;
21
}
22
}
Copied!

Main Entity Class

The main entity class, that is the class with the field(s) we are going to translate, must define:
  • a TranslatedField for the “name” field
  • a TranslationsAssociationField, with a reference to the ExampleTranslationDefinition
<plugin root>/src/Core/Content/Example/ExampleDefinition.php
1
<?php declare(strict_types=1);
2
3
namespace Swag\BasicExample\Core\Content\Example;
4
5
use ...
6
7
class ExampleDefinition extends EntityDefinition
8
{
9
public const ENTITY_NAME = 'example';
10
11
public function getEntityName(): string
12
{
13
return self::ENTITY_NAME;
14
}
15
16
[...]
17
18
protected function defineFields(): FieldCollection
19
{
20
return new FieldCollection([
21
(new IdField('id', 'id'))->addFlags(new PrimaryKey(), new ApiAware(), new Required()),
22
(new StringField('not_translated_field', 'notTranslatedField'))->addFlags(new ApiAware()),
23
(new TranslatedField('name'))->addFlags(new ApiAware(), new Required()),
24
(new TranslationsAssociationField(
25
ExampleTranslationDefinition::class,
26
'swag_example_id'
27
))->addFlags(new ApiAware(), new Required())
28
]);
29
}
30
}
31
32
</div>
Copied!