janwebdev / translatable-entity-bundle
Make Entity translatable
Installs: 116
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
Type:symfony-bundle
Requires
- php: ^7.4|^8.0|^8.1
- doctrine/orm: ^2.5
- symfony/config: ^4.4||^5.4||^6
- symfony/dependency-injection: ^4.4||^5.4||^6
- symfony/framework-bundle: ^4.4||^5.4||^6
Requires (Dev)
- phpro/grumphp: ^1.3
- phpstan/phpstan: ^1.7.10
- phpunit/phpunit: ^9
- roave/security-advisories: dev-latest
- squizlabs/php_codesniffer: ^3.5
- symfony/test-pack: *
Conflicts
README
Prerequisites
- Installation
- Enable the Bundle
- Entities
- Repository
- Form
- Customize the behaviour for default translation and translation not found
1. Installation
Using composer
Run the composer to download the bundle:
$ composer require janwebdev/translatable-entity-bundle
2. Enable the bundle
Check if bundle was enabled:
<?php // ./config/bundles.php return [ // ... Janwebdev\TranslatableEntityBundle\TranslatableEntityBundle::class => ['all' => true], ];
3. Entities
<?php namespace App\Entity; use Doctrine\Common\Collections\ArrayCollection; class Article { /** * @ORM\OneToMany(targetEntity="ArticleTranslation", mappedBy="translatable", cascade={"persist"}) */ protected $translations; public function __construct() { $this->translations = new ArrayCollection(); } }
<?php namespace App\Entity; use Doctrine\Common\Collections\ArrayCollection; class ArticleTranslation { /** * * @ORM\Column(name="locale", type="string", length=128) */ private $locale; /** * @ORM\ManyToOne(targetEntity="Article", inversedBy="translations") * @ORM\JoinColumn(name="article_id", referencedColumnName="id", onDelete="CASCADE") */ protected $translatable; /** * * @ORM\Column(name="name", type="string", length=128) */ private $name; }
Then run doctrine:generate:entities and remove the generated method addTranslation (for Article in this example). Now you can implement the correct interfaces.
<?php namespace App\Entity; use Janwebdev\TranslatableEntityBundle\Model\TranslatableWrapper; class Article extends TranslatableWrapper { }
TranslatableWrapper extends Translatable (that implements some methods of TranslatableInterface) and adds a magic method to wrap the methods of TranslatingInterface entity.
<?php namespace App\Entity; use Janwebdev\TranslatableEntityBundle\Model\TranslatingInterface; use Janwebdev\TranslatableEntityBundle\Model\TranslatableInterface; class ArtcileTranslation implements TranslatingInterface { }
Remember to modify the signature for ArticleTranslation::setTranslatable
If you want "kill the magic", you can extend directly Janwebdev\TranslatableEntityBundle\Model\Translatable and create the needed wrapper methods by yourself.
4. Repository
<?php namespace App\Repository; use Doctrine\ORM\EntityRepository; class ArticleRepository extends EntityRepository { public function getArticleTranslatedQuery() { $qb = $this->createQueryBuilder('a') ->select(array('a', 'at')) ->leftJoin('a.translations', 'at'); return $qb; } }
5. Form
A possible form implementation
<?php namespace App\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; class ArtcileTranslationFormType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('locale', 'hidden'); $builder->add('name'); } public function setDefaultOptions(array $options) { return array( 'data_class' => 'App\Entity\ArtcileTranslation', ); } }
<?php namespace App\Form; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; class ArticleType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('translations', 'collection', array( 'type' => new ArtcileTranslation 'by_reference' => false, )); } }
6. Customize the behaviour for default translation and translation not found
If you want use as default translation, the first found translation even if is not a translation for current locale neither for default locale
protected function acceptFirstTransaltionAsDefault() { return true; }
If you want a translation, only if was found one for the current locale
protected function acceptDefaultLocaleTransaltionAsDefault() { return false; }
If you want manage by yourself the behaviour in case of translation not found
protected function handleTranslationNotFound() { //your logic }
for example
protected function handleTranslationNotFound() { $class = get_class($this) . 'Translation'; if (class_exists($class)) { $this->translation = new $class; } else { $this->translation = null; } }
Unit tests
$ phpunit
Changelog
Please see CHANGELOG for more information what has changed recently.
License
The MIT License (MIT). Please see License File for more information.