-
-
Save pakaufmann/2701676 to your computer and use it in GitHub Desktop.
Changed a couple of things. Languages are now dynamic and a default language can be used which won't be written into a translation object but directly into the object itself so it can be used like in this blog post: http://knplabs.fr/blog/I%28blah...blah.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Edge5\TestprojectBundle\Form; | |
use Symfony\Component\Form\Event\DataEvent; | |
use Symfony\Component\Form\FormFactoryInterface; | |
use Symfony\Component\EventDispatcher\EventSubscriberInterface; | |
use Symfony\Component\Form\FormEvents; | |
use Symfony\Component\DependencyInjection\ContainerInterface; | |
use Symfony\Component\Form\FormError; | |
class AddTranslatedFieldSubscriber implements EventSubscriberInterface | |
{ | |
private $factory; | |
private $options; | |
private $container; | |
public function __construct(FormFactoryInterface $factory, ContainerInterface $container, Array $options) | |
{ | |
$this->factory = $factory; | |
$this->options = $options; | |
$this->container = $container; | |
} | |
public static function getSubscribedEvents() | |
{ | |
// Tells the dispatcher that we want to listen on the form.pre_set_data | |
// , form.post_data and form.bind_norm_data event | |
return array( | |
FormEvents::PRE_SET_DATA => 'preSetData', | |
FormEvents::POST_BIND => 'postBind', | |
FormEvents::BIND_NORM_DATA => 'bindNormData' | |
); | |
} | |
private function bindTranslations($data) | |
{ | |
//Small helper function to extract all Personal Translation | |
//from the Entity for the field we are interested in | |
//and combines it with the fields | |
$collection = array(); | |
$availableTranslations = array(); | |
foreach($data as $Translation) | |
{ | |
//if the translations object is null, skip it (happens when a subform is called on a new entity) | |
if($Translation == null) | |
{ | |
return; | |
} | |
if(strtolower($Translation->getProperty()) == strtolower($this->options['field'])) | |
{ | |
$availableTranslations[ strtolower($Translation->getLocale()) ] = $Translation; | |
} | |
} | |
foreach($this->getFieldNames() as $locale => $fieldName) | |
{ | |
if(isset($availableTranslations[ strtolower($locale) ])) | |
{ | |
$Translation = $availableTranslations[ strtolower($locale) ]; | |
} | |
else | |
{ | |
$Translation = $this->createPersonalTranslation($locale, $this->options['field'], NULL); | |
} | |
$collection[] = array( | |
'locale' => $locale, | |
'fieldName' => $fieldName, | |
'translation' => $Translation, | |
); | |
} | |
return $collection; | |
} | |
private function getFieldNames() | |
{ | |
//helper function to generate all field names in format: | |
// '<locale>' => '<field>|<locale>' | |
$collection = array(); | |
foreach($this->options['locales'] as $locale) | |
{ | |
$collection[ $locale ] = $this->options['field'] ."|". $locale; | |
} | |
return $collection; | |
} | |
private function createPersonalTranslation($locale, $field, $content) | |
{ | |
//creates a new Personal Translation | |
$className = $this->options['personal_translation']; | |
$Translation = new $className(); | |
$Translation->setLocale($locale); | |
$Translation->setProperty($field); | |
$Translation->setValue($content); | |
return $Translation; | |
} | |
public function bindNormData(DataEvent $event) | |
{ | |
//Validates the submitted form | |
$data = $event->getData(); | |
$form = $event->getForm(); | |
$validator = $this->container->get('validator'); | |
//if the form count is null, skip it (happens when a subform is called on a new entity) | |
if($form->count() == 0) | |
{ | |
return; | |
} | |
foreach($this->getFieldNames() as $locale => $fieldName) | |
{ | |
$content = $form->get($fieldName)->getData(); | |
if( | |
NULL === $content && | |
in_array($locale, $this->options['required_locale'])) | |
{ | |
$form->addError(new FormError(sprintf("Field '%s' for locale '%s' cannot be blank", $this->options['field'], $locale))); | |
} | |
else | |
{ | |
$Translation = $this->createPersonalTranslation($locale, $fieldName, $content); | |
$errors = $validator->validate($Translation, array(sprintf("%s:%s", $this->options['field'], $locale))); | |
if(count($errors) > 0) | |
{ | |
foreach($errors as $error) | |
{ | |
$form->addError(new FormError($error->getMessage())); | |
} | |
} | |
} | |
} | |
} | |
public function postBind(DataEvent $event) | |
{ | |
//if the form passed the validation then set the corresponding Personal Translations | |
$form = $event->getForm(); | |
$data = $form->getData(); | |
$entity = $form->getParent()->getData(); | |
//if the entity is null, skip it (happens when the subform is called with a new entity instead of an updated one) | |
if($entity == null) | |
{ | |
return; | |
} | |
foreach($this->bindTranslations($data) as $binded) | |
{ | |
$content = $form->get($binded['fieldName'])->getData(); | |
$Translation = $binded['translation']; | |
//if default translation don't add the translation, instead write it directly into the entity | |
if($Translation->getLocale() === $this->options['default_locale']) | |
{ | |
$setFunction = 'set'.$Translation->getProperty(); | |
$entity->$setFunction($content); | |
} | |
else | |
{ | |
// set the submitted content | |
$Translation->setValue($content); | |
//test if its new | |
if($Translation->getId()) | |
{ | |
//Delete the Personal Translation if its empty | |
if(NULL === $content && $this->options['remove_empty']) | |
{ | |
$data->removeElement($Translation); | |
if($this->options['entity_manager_removal']) | |
{ | |
$this->container->get('doctrine.orm.entity_manager')->remove($Translation); | |
} | |
} | |
} | |
elseif(NULL !== $content) | |
{ | |
$class = explode('\\', get_class($Translation)); | |
$translationFunction = 'add'.end($class); | |
$entity->$translationFunction($Translation); | |
if(! $data->contains($Translation)) | |
{ | |
$data->add($Translation); | |
} | |
} | |
} | |
} | |
} | |
public function preSetData(DataEvent $event) | |
{ | |
//Builds the custom 'form' based on the provided locales | |
$data = $event->getData(); | |
$form = $event->getForm(); | |
// During form creation setData() is called with null as an argument | |
// by the FormBuilder constructor. We're only concerned with when | |
// setData is called with an actual Entity object in it (whether new, | |
// or fetched with Doctrine). This if statement let's us skip right | |
// over the null condition. | |
if (null === $data) | |
{ | |
return; | |
} | |
$entity = $form->getParent()->getData(); | |
foreach($this->bindTranslations($data) as $binded) | |
{ | |
$t = $binded['translation']; | |
if($t->getLocale() === $this->options['default_locale']) | |
{ | |
//get it out of the actual entity instead of the translation object | |
$getFunction = 'get'.$t->getProperty(); | |
$translation = $entity->$getFunction(); | |
} | |
else | |
{ | |
$translation = $binded['translation']->getValue(); | |
} | |
$form->add($this->factory->createNamed( | |
$this->options['widget'], | |
$binded['fieldName'], | |
$translation, | |
array( | |
'label' => $binded['locale'], | |
'required' => in_array($binded['locale'], $this->options['required_locale']), | |
'property_path'=> false, | |
) | |
)); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
services: | |
form.type.translatable: | |
class: ExampleBundle\Form\TranslatedFieldType | |
arguments: [ @service_container ] | |
tags: | |
- { name: form.type, alias: translatable_field } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace ExampleBundle\Form; | |
use Symfony\Component\Form\AbstractType; | |
use Symfony\Component\Form\FormBuilder; | |
use Symfony\Component\DependencyInjection\ContainerInterface; | |
use Edge5\TestProjectBundle\Form\addTranslatedFieldSubscriber; | |
class TranslatedFieldType extends AbstractType | |
{ | |
protected $container; | |
public function __construct(ContainerInterface $container) | |
{ | |
$this->container = $container; | |
} | |
public function buildForm(FormBuilder $builder, array $options) | |
{ | |
if(! class_exists($options['personal_translation'])) | |
{ | |
Throw new \InvalidArgumentException(sprintf("Unable to find personal translation class: '%s'", $options['personal_translation'])); | |
} | |
if(! $options['field']) | |
{ | |
Throw new \InvalidArgumentException("You should provide a field to translate"); | |
} | |
$subscriber = new addTranslatedFieldSubscriber($builder->getFormFactory(), $this->container, $options); | |
$builder->addEventSubscriber($subscriber); | |
} | |
public function getDefaultOptions(array $options = array()) | |
{ | |
$options['remove_empty'] = true; //Personal Translations without content are removed | |
$options['default_locale'] = 'en'; //the default language to use | |
$options['csrf_protection'] = false; | |
$options['personal_translation'] = false; //Personal Translation class | |
$options['locales'] = array('en'); //the locales you wish to edit | |
$options['required_locale'] = array('en'); //the required locales cannot be blank | |
$options['field'] = false; //the field that you wish to translate | |
$options['widget'] = "text"; //change this to another widget like 'texarea' if needed | |
$options['entity_manager_removal'] = true; //auto removes the Personal Translation thru entity manager | |
return $options; | |
} | |
public function getName() | |
{ | |
return 'translator'; | |
} | |
} |
Tii
commented
Jun 25, 2012
via email
my entity mapping is a regular one to many exactly like the knp tutorial, I
even added the JoinColumn directive.
Without my line, editing an existing translation worked but when creating
one, i had NULL in the translation_id foreign key.
…On Jun 25, 2012 9:02 AM, "Patrick Kaufmann" < ***@***.***> wrote:
Hm, I never had a problem with the translation relation not getting set
correctly.
What does your entity mapping information look like?
---
Reply to this email directly or view it on GitHub:
https://gist.github.com/2701676
Yeah sorry, my bad. I had this problem with all one-to-many relationships in Sonata. I fixed this problem by overwriting the prePersist and preUpdate methods in the Sonata Admin class. There I loop over all translations and set the translatable to the correct entity.
and I keep getting
The options "field", "personal_translation" do not exist
weird… what am I doing wrong?
oh, I've just returned an array in setDefaultOptions :)
you need to do $resolver->setDefaults();
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment