Vse o WEB
Информация и размышления о Web технологиях

Переводимый контент в Symfony2

Всем привет, сегодня продолжаем тему Symfony. Иногда наше приложения должно содержать материалы, контент которых должен отображаться на нескольких языках. Здесь можно создать несколько сущностей и в каждой сохранить контент для определенной версии сайта. Но есть и другой способ - Doctrine extensions. Сегодня мы поговорим о Translatable - расширении для Doctrine, позволяющем нам иметь сущности с контентом на разных языках.

Самый простой способ подключить это расширение - установить пакет StofDoctrineExtensionsBundle. Как это сделать, читаем здесь. Есть и другой способ, более сложный, о нем можно почитать здесь, но это материал для отдельной статьи.

Итак, для начала создаем новый бранч:

 

$ git checkout -b feature-translatable-content

 

Далее устанавливаем бандл с расширениями Doctrine.

 

$ composer require stof/doctrine-extensions-bundle

 

Следующий шаг - подключение бандла:

 

// app/AppKernel.php

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            // ...

            new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
        );

        // ...
    }

    // ...
}

 

Ну вот, бандл с раширениями уже установлен, но для того, чтобы использовать Translatable, добавим в конфиг следующий код:

 

doctrine:
  orm:
    entity_managers:
      default:
        mappings:
          gedmo_translatable:
            type: annotation
            prefix: Gedmo\Translatable\Entity
            dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity"
            alias: GedmoTranslatable # (optional) it will default to the name set for the mapping
            is_bundle: false

          # сюда же добавляем маппин для других необходимых расширений
          gedmo_sluggable:
            ...

stof_doctrine_extensions:
  default_locale: ru_RU
  orm:
    default:
      translatable: true

 

Если же вы используете короткий синтаксис конфигурации, то  mappings должны быть сразу под ключем orm.

Теперь, когда все необходимые настройки произведены, пришло время привести пример сущности с переводимым контентом. У нас это будет сущность поста блога:

 

<?php
namespace Entity;

use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Translatable\Translatable;

/**
 * @ORM\Table(name="articles")
 * @ORM\Entity
 */
class Post implements Translatable
{
    /** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
    private $id;

    /**
     * @Gedmo\Translatable
     * @ORM\Column(length=128)
     */
    private $title;

    /**
     * @Gedmo\Translatable
     * @ORM\Column(type="text")
     */
    private $content;

    /**
     * @Gedmo\Locale
     * Используется для переопределения локали Translation слушателя
     * это простое свойство класса и не отображает поле в базе данных
     * определять это свойство не обязательно, поскольку оно будет автоматически
     * установлено Translation слушателем
     */
    private $locale;

    // геттеры и сеттеры
    ...

    public function setTranslatableLocale($locale)
    {
        $this->locale = $locale;
    }
}

 

Создадим объект нового поста и наполним его контентом. Поскольку у нас локаль по умолчанию ru_RU (установлена слушателем Translation), то наполнять пост русским контентом будем так:

 

<?php

$em = $this->getDoctrine()->getManager();  // получаем менеджер сущностей из внутри контроллера

$post = new Entity\Post;
$post->setTitle('название на русском');
$post->setContent('контент на русском');
$em->persist($post);
$em->flush();

 

Давайте теперь наполним пост контентом, например, на английском языке:

 

<?php

$em = $this->getDoctrine()->getManager();  // получаем менеджер сущностей из внутри контроллера

// сначала загрузим объект поста из базы даных
$postId = 1;
$post = $em->find('Entity\Post', $postId);
$post->setTitle('название на английском');
$post->setContent('контент на английском');
$post->setTranslatableLocale('en_GB'); // изменяем локаль
$em->persist($post);
$em->flush();

 

Это действие обновит наш пост в базе данных и добавит к нему английский перевод. Вот так вот просто.

Хорошо, добавлять сущности с контентом на разных языках мы уже научились, теперь давайте разберемся, как загрузить их для отображения:

 

<?php

$em = $this->getDoctrine()->getManager();  // получаем менеджер сущностей из внутри контроллера

// найдем нашу статью
$postId = 1;
$post = $em->find('Entity\Post', $postId); // в данный момент все геттеры вернут контент на русском языке
$post->setTranslatableLocale('en_GB');     // изменяем язык поста
$em->refresh($post);                       // обновляем объект, теперь весь контент на английском

 

Если нам нужно получить все переводы конкретной статьи, используем специальный репозиторий Translatation:

 

<?php

$em = $this->getDoctrine()->getManager();  // получаем менеджер сущностей из внутри контроллера

$post = $em->find('Entity\Article', 1 /*article id*/);
$repository = $em->getRepository('Gedmo\Translatable\Entity\Translation');
$translations = $repository->findTranslations($post);

/* $translations содержит:
Array (
    [ru_RU] => Array
        (
            [title] => название на русском
            [content] => контент на русском
        )

    [en_GB] => Array
        (
            [title] => название на английском
            [content] => контент на английском
        )
)*/

 

На практике очень часто мы должны сразу сохранить переводы для одной статьи. Например, в редакторе у нас есть несколько вкладок для разных языковых версий статьи и при отправке формы мы должны сразу сохранить в базу все переводы и саму сущность. Удобнее это делать с помощью мультивставки:

 

<?php

// вставка нескольких переводов, предполагаем что RU - локаль по умолчанию
$repository = $em->getRepository('Gedmo\\Translatable\\Entity\\Translation');

// это справедливо также для ODM
$post = new Article;
$post->setTitle('Моя статья RU');
$post->setContent('контент RU');

$repository
    ->translate($post, 'title', 'en', 'Моя статья EN')
    ->translate($post, 'content', 'en', 'контент EN')
    ->translate($post, 'title', 'de', 'Моя статья DE')
    ->translate($post, 'content', 'de', 'контент DE');

$em->persist($post);
$em->flush();

// обновление той же статьи с добавлением нового перевода

$repository
    ->translate($post, 'title', 'lt', 'Моя статья LT')
    ->translate($post, 'content', 'lt', 'контент LT')
    ->translate($post, 'title', 'en', 'Моя статья EN')
    ->translate($post, 'content', 'en', 'контент EN')
    ->translate($post, 'title', 'de', 'Моя статья DE')
    ->translate($post, 'content', 'de', 'контент DE');

$em->flush();

 

Мы даже можем использовать Translatable в кастомных запросах DoctrineDbal, но эта тема заслуживает отдельной статьи. В документации она подробно расписана и, если будет надобность, интересующийся читатель сможет прочитать и разобраться.

На сегодня достаточно материала, в следующей статье мы рассмотрим другие плагины для Doctrine, которые будут полезны: Sluggable, LoggableTree и другие. До новых встреч!

Наверх