Offers the possibility to easily manage the translatable fields of your entity with a new form type: 'a2lix_translations'.
- Symfony 2.1
- StofDoctrineExtensionsBundle (auto installed from composer)
Add the repository to your composer.json
"a2lix/translation-form-bundle": "dev-master"
Run Composer to install the bundle
php composer.phar update a2lix/translation-form-bundle
Enable the bundle in AppKernel.php
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(), // Check its existing or add
new A2lix\TranslationFormBundle\A2lixTranslationFormBundle(),
Configure the bundle in config.yml
# Check its existing or add
stof_doctrine_extensions:
default_locale: %locale%
orm:
default:
translatable: true
a2lix_translation_form:
object_manager: # [optional] Defaults to doctrine.orm.entity_manager. Name of the object manager. For instance, 'doctrine.orm.default_entity_manager' or 'doctrine_mongodb.odm.default_document_manager'
locales: [fr, es, de] # [optional] Array of the translation locales (The default locale have to be excluded). Can also be specified in the form builder.
default_required: false # [optional] Defaults to false. In this case, translation fields are not mark as required with HTML5.
use_aop: true # [optional] Defaults to false.
# Template
twig:
form:
resources:
- 'A2lixTranslationFormBundle::form.html.twig'
<?php
namespace Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Entity\Product.php
*
* @ORM\Table()
* @Gedmo\TranslationEntity(class="Translation\ProductTranslation")
*/
class Product
{
/**
* @var integer $id
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string $title
*
* @ORM\Column(name="title", type="string", length=255)
* @Gedmo\Translatable
*/
private $title;
/**
* @var string $description
*
* @ORM\Column(name="description", type="text")
* @Gedmo\Translatable
*/
private $description;
/**
* @ORM\OneToMany(
* targetEntity="Translation\ProductTranslation",
* mappedBy="object",
* cascade={"persist", "remove"}
* )
* @Assert\Valid(deep = true)
*/
private $translations;
public function __construct()
{
$this->translations = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* @param string $title
* @return Product
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set description
*
* @param string $description
* @return Product
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set translations
*
* @param ArrayCollection $translations
* @return Product
*/
public function setTranslations($translations)
{
foreach ($translations as $translation) {
$translation->setObject($this);
}
$this->translations = $translations;
return $this;
}
/**
* Get translations
*
* @return ArrayCollection
*/
public function getTranslations()
{
return $this->translations;
}
}
<?php
namespace Entity\Translation;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation;
/**
* Entity\Translation\ProductTranslation.php
* @ORM\Entity
* @ORM\Table(name="product_translations",
* uniqueConstraints={@ORM\UniqueConstraint(name="lookup_unique_idx", columns={
* "locale", "object_id", "field"
* })}
* )
*/
class ProductTranslation extends AbstractPersonalTranslation
{
/**
* @ORM\ManyToOne(targetEntity="Entity\Product", inversedBy="translations")
* @ORM\JoinColumn(name="object_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $object;
}
Minimal form example:
$builder
->add('title')
->add('description')
->add('translations', 'a2lix_translations')
;
Advanced form example:
$builder
->add('translations', 'a2lix_translations', array(
'locales' => array('fr', 'es', 'de'), // [optional|required - depends on the presence in config.yml] See above
'required' => true, // [optional] Overrides default_required if need
'fields' => array( // [optional] Manual configuration of fields to display and options. If not specified, all translatable fields will be display, and options will be auto-detected
'title' => array(
'label' => 'name', // [optional] Custom label. Ucfirst, otherwise
'type' => 'textarea', // [optional] Custom type
**OTHER_OPTIONS** // [optional] max_length, required, trim, read_only, constraints, ...
),
'description' => array(
'label' => 'Desc.', // [optional]
'locale_options' => array( // [optional] Manual configuration of field for a dedicated locale -- Higher priority
'es' => array(
'label' => 'descripción' // [optional] Higher priority
**OTHER_OPTIONS** // [optional] Same possibilities as above
),
'fr' => array(
'display' => false // [optional] Prevent display of the field for this locale
)
)
),
);
))
;
Separate the render of the default locale away from tabs of locales translation.
{{ form_widget(form.title) }}
{{ form_widget(form.description) }}
{{ form_widget(form.translations) }}
or group all locales (default and translations) in a tabs way:
{{ form_widget(form.translations, {'fields': [form.title, form.description]}) }}
There is an article with an example on how to manage translations with SonataAdminBundle available on Elao's blog.
- DoctrineExtensions & StofDoctrineExtensionsBundle
- Contributors: Tristan BESSOUSSA