Skip to content

Instantly share code, notes, and snippets.

@pulse00
Created March 6, 2012 09:16
Show Gist options
  • Save pulse00/1985208 to your computer and use it in GitHub Desktop.
Save pulse00/1985208 to your computer and use it in GitHub Desktop.
Sonata Translatable Admin classes
<?php
class PageAdmin extends BaseAdmin
{
protected function configureFormFields(FormMapper $formMapper)
{
parent::configureFormFields($formMapper);
$page = $formMapper->getAdmin()->getSubject();
foreach (Page::translationFields() as $field) {
$options = array(
'data' => $page,
'locales' => array('en', 'de'),
);
if ($field != 'name') {
$options['type'] = 'textarea';
}
$formMapper->remove($field)->add($field, 'sonata_type_translatable', $options);
}
}
public function preUpdate($object)
{
$this->translate($object);
parent::preUpdate($object);
}
public function prePersist($object)
{
$this->translate($object);
parent::prePersist($object);
}
private function translate(Page $page)
{
// translationFields() simply returns an array
// of names of the fields to be translated, e.g. array('name', 'description')
foreach (Page::translationFields() as $field) {
$getter = 'get' . ucfirst($field);
foreach ($page->{$getter}() as $key => $value) {
$parts = explode('_', $key);
$name = $parts[0];
$locale = $parts[1];
$page->translate($locale)->{$name} = $value;
}
}
foreach (Page::translationFields() as $field) {
$getter = 'get' . ucfirst($field);
if ( is_array($page->{$getter}()) ) {
$setter = 'set' . ucFirst($field);
$page->{$setter}('');
}
}
}
}
# register the translatable type as a service
parameters:
dubture_core.page.form.type.translatable.class: Application\Sonata\PageBundle\Form\Type\TranslatableType
services:
dubture_core.page.form.type.translatable:
class: %dubture_core.page.form.type.translatable.class%
tags:
- { name: form.type, alias: sonata_type_translatable }
<?php
class TranslatableType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
// creates an input field for each locale with the name
// fieldName_locale
foreach ($options['locales'] as $locale) {
$name = $builder->getName() . '_' . $locale;
$builder->add($name, $options['type'], $opts);
}
}
public function getDefaultOptions(array $options)
{
return array_merge($options, array('type' => null));
}
public function getName()
{
return 'sonata_type_translatable';
}
}
@matteosister
Copy link

hi!
Thanks for sharing...
this is not working for me. Firstly, in the TranslatableType there is no $opts var...and than the form gets an error because it looks for a get{Field}De to populate the default value...
Any idea?

@pulse00
Copy link
Author

pulse00 commented Apr 3, 2012

I forgot to remove the $opts variable from the gist, it's simply an array for the 3rd FormBuilder::add argument, something like:

<?php 
       $opts = array('attr' => array('class' => 'aloha'));

As for the get{Field}De problem, this is one major awkwardness of this solution, as you need to add these methods for each field and each locale.

<?php
  public function getNameDe() { return $this->translate('de')->getName(); }
  public function setNameDe($name) {  $this->translate('de')->setName($name); return $this } 

All in all i think this is not a really proper way to do the database translations together with sonata - that's why i'll probably switch to the Symfony CMF and jackrabbit

@cifren
Copy link

cifren commented Apr 4, 2012

For resolving this error i have changed like below and i have added a TranslationProxy :

<?php
class TranslatableType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        // creates an input field for each locale with the name
        // fieldName_locale
        foreach ($options['locales'] as $locale) {

            //here modification BEGIN
            $name = $locale.'Translation.'.$builder->getName() ;
            //here modification END

            $builder->add($name, $options['type'], array());
        }
    }

    public function getDefaultOptions(array $options)
    {
        return array_merge($options, array('type' => null));
    }

    public function getName()
    {
        return 'sonata_type_translatable';
    }
}

This post can help too http://knplabs.fr/blog/I%28blah...blah...blah%29n

@matteosister
Copy link

@cifren this sounds like a good solution...the problem is that the builder do not accept a name like itTranslation.title. It tells:

The name "itTranslation.title" contains illegal characters. Names should start with a letter, digit or underscore and only contain letters, digits, numbers, underscores ("_"), hyphens ("-") and colons (":").

Maybe this is related to the fact that I'm on symfony master? Maybe the form component changed in some way?

@matteosister
Copy link

by now my solution is adding two magic methods to che entity:

    public function __get($property)
    {
        if (preg_match('/(.*)_(.{2})/', $property, $matches)) {
            $prop = $matches[1];
            $lang = $matches[2];
            $method = 'get'.ucfirst($prop);
            return $this->translate($lang)->{$method}();
        }
    }

    public function __set($property, $value)
    {
        if (preg_match('/(.*)_(.{2})/', $property, $matches)) {
            $prop = $matches[1];
            $lang = $matches[2];
            $method = 'set'.ucfirst($prop);
            $this->translate($lang)->{$method}($value);
        }
    }

and in the admin class simply call:

$form
    ->add('title', 'text')
    ->add('title_en', 'text')

@cifren
Copy link

cifren commented May 28, 2012

At the end I try other else

Create an array of the translation and give it to Form and get back and create all the translation object just after like this :

In the Entity :

<?php
....
     public function getArrayTranslations($default_locale = 'en', $all_locale_translation = null) {

        $all_locale_translation = (!$this->all_locale_translation)? $default_locale : $this->all_locale_translation;
        $default_locale = (!$this->default_locale)? $default_locale : $this->default_locale;

        $arrayTranslations = array();

        foreach($all_locale_translation as $locale)
        {
            foreach(self::translationFields() as $property)
            {
                $getter = 'get'.lcfirst($property);
                $arrayTranslations[$locale][$property] = $this->translate(($locale==$default_locale)?null:$locale)->$getter();
            }
        }

        return $arrayTranslations;
    }

    public function setArrayTranslations($array, $default_locale = 'en', $all_locale_translation = null) {

        $all_locale_translation = (!$this->all_locale_translation)? $default_locale : $this->all_locale_translation;
        $default_locale = (!$this->default_locale)? $default_locale : $this->default_locale;

        foreach($array as $locale => $values)
        {
            foreach($values as $property => $value)
            {
                $setter = 'set'.lcfirst($property);
                $this->translate(($locale==$default_locale)?null:$locale)->$setter($value);
            }
        }

        return $this;
    }

    public static function translationFields() {
        return array('title', 'blog', 'tags', 'intro');
    }
....

And in the Admin Form :

<?php
   public function __construct($code, $class, $baseControllerName, ObjectManager $om, $default_locale, $all_locale)
    {
        parent::__construct($code, $class, $baseControllerName);
        $this->om             = $om;        
        $this->default_locale = $default_locale;
        $this->all_locale = $all_locale;
    }
    protected function configureFormFields(FormMapper $formMapper)
    { 
        ....

        ->add('array_translations', 'collection', 
            array(
               'type' => new BlogTranslationType()
            )
        )

        ....
     }

And BlogTranslationType

<?php
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder
            ->add('title')
            ->add('intro', 'textarea)
            ->add('blog', 'textarea')
            ->add('tags')
        ;
    }

And that's it !!!

After I added somefeature for the language option to get all the information from the config.yml
=> you have an example on this GIT https://github.com/cifren/BlogPerso/blob/master/src/Blogger/BlogBundle/Admin/BlogAdmin.php

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment