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.
INFO
Refer to this video on Translating your entity that deals with data translations. Also available on our free online training "Shopware 6 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
<?php declare(strict_types=1);
namespace Swag\BasicExample\Migration;
use Doctrine\DBAL\Connection;
use Shopware\Core\Framework\Migration\MigrationStep;
class Migration1612863838ExampleTranslation extends MigrationStep
{
public function getCreationTimestamp(): int
{
return 1612863838;
}
public function update(Connection $connection): void
{
$query = <<<SQL
CREATE TABLE IF NOT EXISTS `swag_example_translation` (
`swag_example_id` BINARY(16) NOT NULL,
`language_id` BINARY(16) NOT NULL,
`name` VARCHAR(255),
`created_at` DATETIME(3) NOT NULL,
`updated_at` DATETIME(3) NULL,
PRIMARY KEY (`swag_example_id`, `language_id`),
CONSTRAINT `fk.swag_example_translation.swag_example_id` FOREIGN KEY (`swag_example_id`)
REFERENCES `swag_example` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk.swag_example_translation.language_id` FOREIGN KEY (`language_id`)
REFERENCES `language` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
SQL;
$connection->executeStatement($query);
}
public function updateDestructive(Connection $connection): void
{
}
}
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
<?php declare(strict_types=1);
namespace Swag\BasicExample\Core\Content\Example\Aggregate\ExampleTranslation;
use Shopware\Core\Framework\DataAbstractionLayer\EntityTranslationDefinition;
use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\Required;
use Shopware\Core\Framework\DataAbstractionLayer\Field\StringField;
use Shopware\Core\Framework\DataAbstractionLayer\FieldCollection;
use Swag\BasicExample\Core\Content\Example\ExampleDefinition;
class ExampleTranslationDefinition extends EntityTranslationDefinition
{
public const ENTITY_NAME = 'swag_example_translation';
public function getEntityName(): string
{
return self::ENTITY_NAME;
}
public function getParentDefinitionClass(): string
{
return ExampleDefinition::class;
}
protected function defineFields(): FieldCollection
{
return new FieldCollection([
(new StringField('name', 'name'))->addFlags(new Required()),
]);
}
}
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
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="Swag\BasicExample\Core\Content\Example\ExampleDefinition">
<tag name="shopware.entity.definition" entity="swag_example" />
</service>
<service id="Swag\BasicExample\Core\Content\Example\Aggregate\ExampleTranslation\ExampleTranslationDefinition">
<tag name="shopware.entity.definition" entity="swag_example_translation" />
</service>
</services>
</container>
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
<?php declare(strict_types=1);
namespace Swag\BasicExample\Core\Content\Example\Aggregate\ExampleTranslation;
use Shopware\Core\Framework\DataAbstractionLayer\TranslationEntity;
use Swag\BasicExample\Core\Content\Example\ExampleEntity;
class ExampleTranslationEntity extends TranslationEntity
{
protected string $exampleId;
protected ?string $name;
protected ExampleEntity $example;
public function getExampleId(): string
{
return $this->exampleId;
}
public function setExampleId(string $exampleId): void
{
$this->exampleId = $exampleId;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): void
{
$this->name = $name;
}
public function getExample(): ExampleEntity
{
return $this->example;
}
public function setExample(ExampleEntity $example): void
{
$this->example = $example;
}
}
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
class ExampleTranslationDefinition extends EntityTranslationDefinition
{
[...]
public function getEntityClass(): string
{
return ExampleTranslationEntity::class;
}
}
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
<?php declare(strict_types=1);
namespace Swag\BasicExample\Core\Content\Example\Aggregate\ExampleTranslation;
use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
/**
* @method void add(ExampleTranslationEntity $entity)
* @method void set(string $key, ExampleTranslationEntity $entity)
* @method ExampleTranslationEntity[] getIterator()
* @method ExampleTranslationEntity[] getElements()
* @method ExampleTranslationEntity|null get(string $key)
* @method ExampleTranslationEntity|null first()
* @method ExampleTranslationEntity|null last()
*/
class ExampleTranslationCollection extends EntityCollection
{
protected function getExpectedClass(): string
{
return ExampleTranslationEntity::class;
}
}
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
<?php declare(strict_types=1);
namespace Swag\BasicExample\Core\Content\Example;
use ...
class ExampleDefinition extends EntityDefinition
{
public const ENTITY_NAME = 'example';
public function getEntityName(): string
{
return self::ENTITY_NAME;
}
[...]
protected function defineFields(): FieldCollection
{
return new FieldCollection([
(new IdField('id', 'id'))->addFlags(new PrimaryKey(), new ApiAware(), new Required()),
(new StringField('not_translated_field', 'notTranslatedField'))->addFlags(new ApiAware()),
(new TranslatedField('name'))->addFlags(new ApiAware(), new Required()),
(new TranslationsAssociationField(
ExampleTranslationDefinition::class,
'swag_example_id'
))->addFlags(new ApiAware(), new Required())
]);
}
}