Skip to content

Instantly share code, notes, and snippets.

@mRoca
Created January 14, 2016 16:37
Show Gist options
  • Select an option

  • Save mRoca/8ea898204501b2430d38 to your computer and use it in GitHub Desktop.

Select an option

Save mRoca/8ea898204501b2430d38 to your computer and use it in GitHub Desktop.
Gedmo translatable field
<?php
namespace App\ModuleContractBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class CategoryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'name_trans',
'translatable_field',
array(
'field' => 'name',
'label' => 'contract.category.name',
)
);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'data_class' => 'App\ModuleContractBundle\Entity\Category'
)
);
}
public function getName()
{
return 'app_modulecontractbundle_category';
}
}
<?php
namespace App\ApplicationBundle\Form\Type;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use App\ApplicationBundle\EventListener\Form\TranslatedFieldSubscriber;
class TranslatableFieldType extends AbstractType
{
protected $container;
protected $defaultLocale;
public function __construct(ContainerInterface $container, $defaultLocale)
{
$this->container = $container;
$this->defaultLocale = $defaultLocale;
}
/**
* {@inheritDoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
if (null === $options['field']) {
$options['field'] = $builder->getName();
}
$subscriber = new TranslatedFieldSubscriber(
$builder->getFormFactory(),
$this->container->get('doctrine.orm.entity_manager'),
$this->container->get('validator'),
$this->container->get('App.application.service.culture_helper')->getAvailableLocales(),
$this->defaultLocale,
$options
);
$builder->addEventSubscriber($subscriber);
}
/**
* {@inheritDoc}
*/
public function getName()
{
return 'translatable_field';
}
/**
* {@inheritDoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
// Options
'field' => null, // Set to form builder name if empty
'translation_class' => null, // Use $entity::TRANSLATION_CLASS or Gedmo\Translatable\Entity\Translation if empty
'field_type' => 'text',
// FormType
'mapped' => false,
'field_options' => array(),
)
);
}
}
<?php
namespace App\ApplicationBundle\EventListener\Form;
use Doctrine\ORM\EntityManagerInterface;
use Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation;
use Gedmo\Translatable\Entity\Repository\TranslationRepository;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\ValidatorInterface;
use App\ApplicationBundle\Entity\Translatable\AbstractTranslatable;
class TranslatedFieldSubscriber implements EventSubscriberInterface
{
const DEFAULT_TRANSLATION_CLASS = 'Gedmo\Translatable\Entity\Translation';
protected $formFactory;
protected $em;
protected $validator;
protected $locales;
protected $options;
protected $field;
protected $translationClass;
public function __construct(FormFactoryInterface $formFactory, EntityManagerInterface $em, ValidatorInterface $validator, Array $locales, $defaultLocale, Array $options)
{
$this->formFactory = $formFactory;
$this->em = $em;
$this->validator = $validator;
$this->locales = $locales;
$this->defaultLocale = $defaultLocale;
$this->options = $options;
$this->field = $options['field'];
$this->translationClass = $options['translation_class'];
}
public static function getSubscribedEvents()
{
return array(
FormEvents::PRE_SET_DATA => 'preSetData',
FormEvents::POST_SUBMIT => 'postSubmit',
);
}
/**
* @param AbstractTranslatable|object $entity
* @return string
*/
protected function getTranslationClass($entity)
{
if (null !== $this->translationClass) {
return $this->translationClass;
}
if ($entity instanceof AbstractTranslatable) {
$translationClass = $entity::TRANSLATION_CLASS;
if (null === $translationClass) {
throw new \InvalidArgumentException('The AbstractTranslatable entity must define a TRANSLATION_CLASS');
}
return $translationClass;
}
return self::DEFAULT_TRANSLATION_CLASS;
}
/**
* @param AbstractTranslatable|object $entity
* @return TranslationRepository
*/
protected function getTranslationRepository($entity)
{
$repository = $this->em->getRepository($this->getTranslationClass($entity));
if (!$repository instanceof TranslationRepository) {
throw new \InvalidArgumentException('The repository must be a TranslationRepository');
}
return $repository;
}
/**
* @param AbstractTranslatable|object $entity
* @return array
*/
protected function getTranslations($entity)
{
if ($entity instanceof AbstractTranslatable) {
return $entity->findTranslations($this->defaultLocale);
}
return $this->getTranslationRepository($entity)->findTranslations($entity);
}
/**
* @param AbstractTranslatable|object $entity
* @param string $field
* @return string
*/
protected function getDefaultTranslation($entity, $field)
{
if (!$entity instanceof AbstractTranslatable) {
return null;
}
// Entity not yet persisted
if (null === $this->em->getUnitOfWork()->getSingleIdentifierValue($entity)) {
return null;
}
$entity->setTranslatableLocale($this->defaultLocale);
$this->em->refresh($entity);
$getter = 'get'.ucfirst($field);
return method_exists($entity, $getter) ? call_user_func(array($entity, $getter)) : null;
}
/**
* @param AbstractTranslatable|object $entity
* @param string $field
* @param array $data
*/
protected function setTranslations($entity, $field, $data)
{
if ($entity instanceof AbstractTranslatable) {
$entity->setTranslatableLocale($this->defaultLocale);
foreach ($data as $locale => $content) {
/** @var AbstractPersonalTranslation $translation */
$translationClass = $this->getTranslationClass($entity);
$translation = new $translationClass();
$translation->setField($field);
$translation->setLocale($locale);
$translation->setContent($content);
$entity->addTranslation($translation);
}
return;
}
$repository = $this->getTranslationRepository($entity);
foreach ($data as $locale => $content) {
$repository->translate($entity, $this->field, $locale, $content);
}
}
/**
* @param AbstractTranslatable|object $entity
* @param string $field
* @return Constraint[]
*/
protected function getConstraintsForMember($entity, $field)
{
/** @var ClassMetadata $metadata */
$metadata = $this->validator->getMetadataFor($entity);
$constraints = array();
foreach ($metadata->getPropertyMetadata($field) as $member) {
$constraints += $member->getConstraints();
}
return $constraints;
}
public function preSetData(FormEvent $event)
{
$form = $event->getForm();
$entity = $form->getParent()->getData();
$translations = $this->getTranslations($entity);
foreach ($this->locales as $locale) {
$data = isset($translations[$locale], $translations[$locale][$this->field]) ? $translations[$locale][$this->field] : null;
if (null === $data && $locale === $this->defaultLocale) {
$data = $this->getDefaultTranslation($entity, $this->field);
}
$form->add(
$this->formFactory->createNamed(
$locale,
$this->options['field_type'],
$data,
array(
'label' => $locale,
'auto_initialize' => false,
'constraints' => $this->getConstraintsForMember($entity, $this->field), // Caution with unicity constraint
)
)
);
}
return;
}
public function postSubmit(FormEvent $event)
{
$data = $event->getData();
$entity = $event->getForm()->getParent()->getData();
$this->setTranslations($entity, $this->field, $data);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment