Всем привет, сегодня продолжаем тему 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, Loggable, Tree и другие. До новых встреч!