V tomto článku si ukážeme, jak jednoduše přeložit entity v Doctrine. Navážeme přesně tam, kde jsme skončili v předchozím článku Spojení Nette a Doctrine krok za krokem.
Pro překlady se výborně hodí balíček Doctrine2 behaviors od KnpLabs. Knihovna toho nabízí mnohem více, v tomto článku se zaměříme pouze překlady.
Integraci do Nette řeší Zenify/DoctrineBehaviors. Potřebujeme také Translator, který použijeme z Kdyby. Obě závislosti nainstalujeme přes composer:
composer require kdyby/translation
composer require zenify/doctrine-behaviors
V config.neon
nastavíme translator, zaregistrujeme obě rozšíření a subscriber, který se stará o propojení překladových entit:
extensions:
translation: Kdyby\Translation\DI\TranslationExtension
translatable: Zenify\DoctrineBehaviors\DI\TranslatableExtension
translation:
default: en
whitelist: [cs, en]
translatable:
currentLocaleCallable: [@translation.default, getLocale]
Konfiguraci máme za sebou a můžeme se vrhnout na překlad entit. Ten už je poměrně přímočarý - nejdříve vytvoříme překladovou entitu app/ArticleModule/Entity/ArticleTranslation.php
, do které přesuneme proměnnou $name
a getter i setter:
namespace ArticleModule\Entity;
use Knp\DoctrineBehaviors\Model\Translatable\Translation;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="article_translation")
*/
class ArticleTranslation
{
use Translation;
/**
* @ORM\Column(type="string", nullable=true)
* @var string
*/
private $name;
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
}
Upravíme také app/ArticleModule/Entity/Article.php
, která se podstatně zjednoduší:
namespace ArticleModule\Entity;
use Doctrine\ORM\Mapping as ORM;
use Kdyby\Doctrine\Entities\Attributes\Identifier;
use Knp\DoctrineBehaviors\Model\Translatable\Translatable;
use Zenify\DoctrineBehaviors\Entities\Attributes\Translatable as ZenifyTranslatable;
/**
* @ORM\Entity
* @ORM\Table(name="article")
*
* @method ArticleTranslation translate($lang='')
*/
class Article
{
use Identifier;
use Translatable;
use ZenifyTranslatable;
}
Do entit jsme přidali traity, na které se navěsí (na dříve zaregistrovaný) subscriber a automaticky propojí entity.
Všimněte si také komentáře @method ArticleTranslation translate($lang='') - díky němu bude IDE napovídat při práci s entitou.
Updatneme schema databáze (Pokud dostanete chybu, smažte cache):
php www/index.php o:s:u --dump-sql
CREATE TABLE article_translation (id INT AUTO_INCREMENT NOT NULL, translatable_id INT DEFAULT NULL, name VARCHAR(255) DEFAULT NULL, locale VARCHAR(255) NOT NULL, INDEX IDX_2EEA2F082C2AC5D3 (translatable_id), UNIQUE INDEX article_translation_unique_translation (translatable_id, locale), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
ALTER TABLE article_translation ADD CONSTRAINT FK_2EEA2F082C2AC5D3 FOREIGN KEY (translatable_id) REFERENCES article (id) ON DELETE CASCADE;
ALTER TABLE article DROP name;
Zbývá už jen upravit vytvoření nové entity v ArticleService
:
public function createArticle()
{
$article = new Article;
$article->translate('cs')->setName('Název článku');
$article->translate('en')->setName('Name of article');
$this->entityManager->persist($article);
$article->mergeNewTranslations();
$this->entityManager->flush();
}
Nezapomeňte zavolat metodu mergeNewTranslations()
na entitě, jinak se překlady neuloží. Tato metoda se, zjednodušeně řečeno, stará o mapping překladové a překládané entity.
Překlady se používají jednoduše přes funkci translate($lang). Pokud se neuvede argument, dostaneme překlad dle aktuálně nastaveného jazyka:
$article = $this->articleRepository->find(1);
echo $article->translate()->getName(); // Name of article
echo $article->getName(); // Same as $article->translate()->getName()
Díky Zenify
můžeme k překladu přistupovat i bez metody translate()
, nicméně já preferuji její použití, případně vytvoření getteru v základní entitě. Řešení je to (dle mého názoru) čistší a funguje napovídání v IDE.
public function getArticleName()
{
return $this->translate()->getName();
}
Práce s překlady je díky hotovým balíčkům přímočará. Příklad opět najdete na GitHubu. Někdy příště se podíváme na formuláře a jak je spojit s překlady.
Souvisejicí články:
- Spojení Nette a Doctrine krok za krokem
- Překlad entit v Doctrine2