Translatable
Переклади полів сутності в окремій сутності перекладу — по одному рядку на локаль.
Дві сутності
Для сутності <X> поведінка очікує сутність <X>Translation у тому самому просторі імен: App\Entity\Category і App\Entity\CategoryTranslation.
Сутність перекладу містить поля, що перекладаються, і власний ідентифікатор:
use Doctrine\ORM\Mapping as ORM;
use Ufo\DoctrineBehaviors\Contract\Entity\TranslationInterface;
use Ufo\DoctrineBehaviors\Model\Translatable\TranslationTrait;
#[ORM\Entity]
class CategoryTranslation implements TranslationInterface
{
use TranslationTrait;
#[ORM\Id, ORM\GeneratedValue, ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $name = null;
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): void
{
$this->name = $name;
}
}
Сутність, що перекладається, містить лише поля без перекладу:
use Doctrine\ORM\Mapping as ORM;
use Ufo\DoctrineBehaviors\Contract\Entity\TranslatableInterface;
use Ufo\DoctrineBehaviors\Model\Translatable\TranslatableTrait;
#[ORM\Entity]
class Category implements TranslatableInterface
{
use TranslatableTrait;
#[ORM\Id, ORM\GeneratedValue, ORM\Column]
private ?int $id = null;
}
Мапінг
| Сутність | Що додає бандл |
|---|---|
Category |
OneToMany translations → CategoryTranslation, індекс за locale, cascade: persist, remove, orphanRemoval |
CategoryTranslation |
ManyToOne translatable → Category (колонка translatable_id, ON DELETE CASCADE), рядок locale довжиною 5, унікальний індекс (translatable_id, locale) |
Унікальний індекс називається <таблиця>_unique_translation; якщо назва довша за 63 символи, з 2.0.0 замість неї використовується t_<хеш>_unique_translation. #[ORM\MappedSuperclass] пропускається; в ієрархії наслідування індекс отримує лише коренева сутність перекладу.
Використання
$category = new Category();
$category->translate('uk')->setName('Взуття');
$category->translate('en')->setName('Shoes');
$entityManager->persist($category);
$category->mergeNewTranslations(); // перед flush()
$entityManager->flush();
$category->translate('en')->getName(); // Shoes
$category->translate()->getName(); // поточна локаль
translate($locale = null, $fallbackToDefault = true):
- шукає непорожній переклад для
$locale(без аргументу — поточна локаль); - якщо його немає і
$fallbackToDefault— пробує локаль без регіону (uk_UA→uk), потім локаль за замовчуванням; - якщо нічого не знайдено — створює новий переклад і кладе його в
getNewTranslations().
Нові переклади потрапляють у translations (і в базу) лише після mergeNewTranslations(). Той самий метод прибирає порожні переклади — isEmpty() вважає порожнім переклад, у якого всі властивості, крім id, translatable і locale, порожні.
Інші методи: getTranslations(), setTranslations(iterable), addTranslation(), removeTranslation(), getCurrentLocale() / setCurrentLocale(), getDefaultLocale() / setDefaultLocale().
Локалі
Під час postLoad і prePersist бандл ставить сутності поточну локаль і локаль за замовчуванням з LocaleProviderInterface (див. Налаштування). Поза HTTP-запитом поточна локаль не задається і дорівнює локалі за замовчуванням; початкове значення локалі за замовчуванням у трейті — en.
Колонка locale має довжину 5 — локалі на кшталт sr_Latn чи zh_Hant у неї не вміщаються. Див. Відомі помилки.
Проксі до перекладу
proxyCurrentLocaleTranslation() викликає метод перекладу поточної локалі; зручно в __call(), щоб $category->getName() (або category.name у Twig) повертав перекладене значення:
public function __call(string $method, array $arguments): mixed
{
return $this->proxyCurrentLocaleTranslation($method, $arguments);
}
Інші назви класів
Щоб назвати сутність перекладу інакше або винести в інший простір імен, перевизначте обидва статичні методи — кожен має повертати клас іншої сторони:
class Category implements TranslatableInterface
{
use TranslatableTrait;
public static function getTranslationEntityClass(): string
{
return \App\Entity\Translation\CategoryTranslation::class;
}
}
class CategoryTranslation implements TranslationInterface
{
use TranslationTrait;
public static function getTranslatableEntityClass(): string
{
return \App\Entity\Category::class;
}
}
Стратегія завантаження
# config/services.yaml
parameters:
doctrine_behaviors_translatable_fetch_mode: EXTRA_LAZY # колекція translations
doctrine_behaviors_translation_fetch_mode: LAZY # зв'язок translatable
| Значення | Поведінка Doctrine |
|---|---|
LAZY |
Завантажує переклади при першому зверненні (за замовчуванням). |
EAGER |
Завантажує разом із сутністю. |
EXTRA_LAZY |
count(), contains() і подібні виклики не завантажують усю колекцію. |
JOIN бандл не додає — змінюється лише стратегія завантаження.