As you know there is no built-in feature in Doctrine2
for translating entities. If you need entities translation you can choose one of several already existing bundles or create the new one. As there is no “the right” way of working with Doctrine2
translations I just want to share my experience.
DoctrineExtensions: translatable
I don’t really like Doctrine2 Translatable extension as:
- the idea of storing each translatable property as different record in
DB
is not a good solution to me; - quite complex logic when we want to display form to edit translations for all languages that we support in application;
- changing default locale causes
DB
data changes; - maybe this is only my opinion by this is not really intuitive for me.
I’ve used this solution only in one project and it’s not something that I remember well.
You can try it but I’m not recommending it. Official documentation can be found here.
KnpLabs\DoctrineBehaviors: translatable
This is my favorite entities translation implementation for Doctrine2
– I couldn’t find better solution so far. Even for project that I was migrating schema from old symfony 1.0
where there were used Propel
translations with Active Record models like Category
and CategoryI18n
– almost everything can be configured and even if there is something that can’t be configured we can just override it.
Note: KNPLabs translatable behaviour is using PHP traits. If you don’t know it already please read the documentation.
In this example I will create simple Post
entity for storing translations in three languages (you can use as many languages as you need). I’m also using Symfony3 framework with its installer to setup new project. We will also need DB connection (mysql
, sqlite
or any other you like).
Install KnpLabsBehaviors
bundle and configure it according to documentation.
Next step is to generate our Post
and PostTranslation
entities for storing translations. Post
will be our main entity and PostTranslation
will be used for storing translations:
1 |
php bin/console doctrine:generate:entity |
Post
entity with properties:
Post->status
Post->created_at
and PostTranslation
entity with properties:
PostTranslation->title
PostTranslation->content
Note: if you want to generate PostTranslation
entity using Doctrine2
commmand you would have to remove PostTranslation->id
property after generation as Knp
is adding it automatically generating also proper relations for translations.
When entities are ready we should add new methods to be able to work with translations. We don’t have to that manually, just to import and use proper traits from Knp
bundle:
- to translated entity add import of:
use Knp\DoctrineBehaviors\Model\Translatable\Translatable;
- to entity which will be storing translation we should add:
use Knp\DoctrineBehaviors\Model\Translatable\Translation;
Then we need to update DB
schema:
1 |
php bin/console doctrine:schema:update --force |
and we can start working with translations:
1 2 3 4 5 6 7 8 9 10 11 |
$post = new \AppBundle\Entity\Post(); $post->translate('pl')->setTitle('Programowanie'); $post->translate('en')->setTitle('Programming'); $em->persist($post); // In order to persist new translations, call mergeNewTranslations method, before flush $post->mergeNewTranslations(); $em->flush(); echo $post->translate('pl')->getTitle(); echo $post->translate('en')->getTitle(); |
Translation interface is simple and intuitive. In similar scenario we would have to add proper relation and persist translation entity. Thanks to Knp
this is handled by calling Post::mergeNewTranslations
method.
For me it is also very useful to be able to call direct method on translated entity based on current locale:
1 2 3 4 5 |
// in Post entity class public function __call($method, $arguments) { return $this->proxyCurrentLocaleTranslation($method, $arguments); } |
Thanks to this method we can simply call:
1 2 3 4 5 |
// will return string: "programowanie" as my default locale is 'pl' echo $post->getTitle(); $post->setCurrentLocale('en')' // now will return string: "programming" echo $post->getTitle(); |
Knp
is shipped with TranslatableSubscriber
which is also responsible for setting current locale on translated entities. We can change that locale by calling setCurrentLocale
on translated entity (Post
in our case).
Note: if you need Doctrine2
entities translations to be managed from Symfony
forms, this implementation works great with A2lix\TranslationFormBundle bundle. I described this integration in dedicated blog post.
Pingback: Translating Doctrine Entities with Symfony forms - Notatki Programisty()