Překlad entit v Doctrine2

05.10.2016| Petr Fidler

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:

Zaujal tě článek a chceš ho nasdílet?