|
@@ -17,6 +17,11 @@ Features:
|
|
|
[blog_reference]: http://gediminasm.org/article/translatable-behavior-extension-for-doctrine-2 "Translatable extension for Doctrine 2 makes automatic record field translations and their loading depending on language used"
|
|
|
[blog_test]: http://gediminasm.org/test "Test extensions on this blog"
|
|
|
|
|
|
+**2012-01-28**
|
|
|
+
|
|
|
+- Created personal translation which maps through real foreign key
|
|
|
+constraint. This dramatically improves the management of translations
|
|
|
+
|
|
|
**2012-01-04**
|
|
|
|
|
|
- Refactored translatable to be able to persist, update many translations
|
|
@@ -55,8 +60,8 @@ and any number of them
|
|
|
- You can [test live][blog_test] on this blog
|
|
|
- Public [Translatable repository](http://github.com/l3pp4rd/DoctrineExtensions "Translatable extension on Github") is available on github
|
|
|
- Using other extensions on the same Entity fields may result in unexpected way
|
|
|
-- May inpact your application performace since it does an additional query for translation
|
|
|
-- Last update date: **2012-01-02**
|
|
|
+- May inpact your application performace since it does an additional query for translation if loaded without query hint
|
|
|
+- Last update date: **2012-01-28**
|
|
|
|
|
|
**Portability:**
|
|
|
|
|
@@ -68,17 +73,20 @@ This article will cover the basic installation and functionality of **Translatab
|
|
|
Content:
|
|
|
|
|
|
- [Including](#including-extension) the extension
|
|
|
-- [Attaching](#event-listener) the **Translation Listener**
|
|
|
-- Entity [example](#entity)
|
|
|
-- Document [example](#document)
|
|
|
-- [Yaml](#yaml) mapping example
|
|
|
-- [Xml](#xml) mapping example
|
|
|
+- [Attaching](#event-listener) the **Translatable Listener**
|
|
|
+- Entity [example](#entity-domain-object)
|
|
|
+- Document [example](#document-domain-object)
|
|
|
+- [Yaml](#yaml-mapping) mapping example
|
|
|
+- [Xml](#xml-mapping) mapping example
|
|
|
- Basic usage [examples](#basic-examples)
|
|
|
- [Persisting](#multi-translations) multiple translations
|
|
|
- Using ORM query [hint](#orm-query-hint)
|
|
|
- Advanced usage [examples](#advanced-examples)
|
|
|
+- Personal [translations](#personal-translations)
|
|
|
|
|
|
-## Setup and autoloading {#including-extension}
|
|
|
+<a name="including-extension"></a>
|
|
|
+
|
|
|
+## Setup and autoloading
|
|
|
|
|
|
If you using the source from github repository, initial directory structure for
|
|
|
the extension library should look like this:
|
|
@@ -132,7 +140,9 @@ $doctrineOrmConfig->setMetadataDriverImpl($chainDriverImpl);
|
|
|
|
|
|
If you need a translation table per single Entity or Document, we will cover how to setup it later
|
|
|
|
|
|
-### Attaching the Translation Listener to the event manager {#event-listener}
|
|
|
+<a name="event-listener"></a>
|
|
|
+
|
|
|
+### Attaching the Translation Listener to the event manager
|
|
|
|
|
|
To attach the **Translation Listener** to your event system and to set the translation locale
|
|
|
to be used in global scope for all Entities or Documents:
|
|
@@ -156,7 +166,9 @@ $evm->addEventSubscriber($translatableListener);
|
|
|
- **@Gedmo\Mapping\Annotation\Locale or @Gedmo\Mapping\Annotation\Language** this will identify this column as **locale** or **language**
|
|
|
used to override the global locale
|
|
|
|
|
|
-## Translatable Entity example: {#entity}
|
|
|
+<a name="entity-domain-object"></a>
|
|
|
+
|
|
|
+## Translatable Entity example:
|
|
|
|
|
|
**Note:** that Translatable interface is not necessary, except in cases there
|
|
|
you need to identify entity as being Translatable. The metadata is loaded only once then
|
|
@@ -169,6 +181,7 @@ namespace Entity;
|
|
|
use Gedmo\Mapping\Annotation as Gedmo;
|
|
|
use Doctrine\ORM\Mapping as ORM;
|
|
|
use Gedmo\Translatable\Translatable;
|
|
|
+
|
|
|
/**
|
|
|
* @ORM\Table(name="articles")
|
|
|
* @ORM\Entity
|
|
@@ -229,7 +242,9 @@ class Article implements Translatable
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-## Translatable Document example: {#document}
|
|
|
+<a name="document-domain-object"></a>
|
|
|
+
|
|
|
+## Translatable Document example:
|
|
|
|
|
|
``` php
|
|
|
<?php
|
|
@@ -238,6 +253,7 @@ namespace Document;
|
|
|
use Gedmo\Mapping\Annotation as Gedmo;
|
|
|
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
|
|
|
use Gedmo\Translatable\Translatable;
|
|
|
+
|
|
|
/**
|
|
|
* @ODM\Document(collection="articles")
|
|
|
*/
|
|
@@ -297,7 +313,9 @@ class Article implements Translatable
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-## Yaml mapping example {#yaml}
|
|
|
+<a name="yaml-mapping"></a>
|
|
|
+
|
|
|
+## Yaml mapping example
|
|
|
|
|
|
Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml**
|
|
|
|
|
@@ -328,7 +346,9 @@ Entity\Article:
|
|
|
- translatable
|
|
|
```
|
|
|
|
|
|
-## Xml mapping example {#xml}
|
|
|
+<a name="xml-mapping"></a>
|
|
|
+
|
|
|
+## Xml mapping example
|
|
|
|
|
|
``` xml
|
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -355,6 +375,8 @@ Entity\Article:
|
|
|
</doctrine-mapping>
|
|
|
```
|
|
|
|
|
|
+<a name="basic-examples"></a>
|
|
|
+
|
|
|
## Basic usage examples: {#basic-examples}
|
|
|
|
|
|
Currently a global locale used for translations is "en_us" which was
|
|
@@ -427,7 +449,9 @@ echo $article->getContent();
|
|
|
// prints: "my content in en"
|
|
|
```
|
|
|
|
|
|
-## Persisting multiple translations {#multi-translations}
|
|
|
+<a name="multi-translations"></a>
|
|
|
+
|
|
|
+## Persisting multiple translations
|
|
|
|
|
|
Usually it is more convinient to persist more translations when creating
|
|
|
or updating a record. **Translatable** allows to do that through translation repository.
|
|
@@ -471,7 +495,9 @@ $repo
|
|
|
$em->flush();
|
|
|
```
|
|
|
|
|
|
-## Using ORM query hint {#orm-query-hint}
|
|
|
+<a name="orm-query-hint"></a>
|
|
|
+
|
|
|
+## Using ORM query hint
|
|
|
|
|
|
By default, behind the scenes, when you load a record - translatable hooks into **postLoad**
|
|
|
event and issues additional query to translate all fields. Imagine that when you load a collection,
|
|
@@ -590,7 +616,9 @@ would override the translation locale used to translate the resultset.
|
|
|
|
|
|
**Note:** all these query hints lasts only for the specific query.
|
|
|
|
|
|
-## Advanced examples: {#advanced-examples}
|
|
|
+<a name="advanced-examples"></a>
|
|
|
+
|
|
|
+## Advanced examples:
|
|
|
|
|
|
### Default locale
|
|
|
|
|
@@ -672,4 +700,175 @@ class Article
|
|
|
|
|
|
Now all translations of Article will be stored and queried from specific table
|
|
|
|
|
|
+<a name="personal-translations"></a>
|
|
|
+
|
|
|
+## Personal translations
|
|
|
+
|
|
|
+Translatable has **AbstractPersonalTranslation** mapped superclass, which must
|
|
|
+be extended and mapped based on your **entity** which you want to translate.
|
|
|
+Note: translations are not automapped because of user preference based on cascades
|
|
|
+or other possible choises, which user can make.
|
|
|
+Personal translations uses foreign key constraint which is fully managed by ORM and
|
|
|
+allows to have a collection of related translations. User can use it anyway he likes, etc.:
|
|
|
+implementing array access on entity, using left join to fill collection and so on.
|
|
|
+
|
|
|
+Note: that [query hint](#orm-query-hint) will work on personal translations the same way.
|
|
|
+You can always use a left join like for standard doctrine collections.
|
|
|
+
|
|
|
+Usage example:
|
|
|
+
|
|
|
+``` php
|
|
|
+<?php
|
|
|
+namespace Entity;
|
|
|
+
|
|
|
+use Doctrine\Common\Collections\ArrayCollection;
|
|
|
+use Gedmo\Mapping\Annotation as Gedmo;
|
|
|
+use Doctrine\ORM\Mapping as ORM;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @ORM\Entity
|
|
|
+ * @Gedmo\TranslationEntity(class="Entity\CategoryTranslation")
|
|
|
+ */
|
|
|
+class Category
|
|
|
+{
|
|
|
+ /**
|
|
|
+ * @ORM\Column(type="integer")
|
|
|
+ * @ORM\Id
|
|
|
+ * @ORM\GeneratedValue
|
|
|
+ */
|
|
|
+ private $id;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @Gedmo\Translatable
|
|
|
+ * @ORM\Column(length=64)
|
|
|
+ */
|
|
|
+ private $title;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @Gedmo\Translatable
|
|
|
+ * @ORM\Column(type="text", nullable=true)
|
|
|
+ */
|
|
|
+ private $description;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ORM\OneToMany(
|
|
|
+ * targetEntity="CategoryTranslation",
|
|
|
+ * mappedBy="object",
|
|
|
+ * cascade={"persist", "remove"}
|
|
|
+ * )
|
|
|
+ */
|
|
|
+ private $translations;
|
|
|
+
|
|
|
+ public function __construct()
|
|
|
+ {
|
|
|
+ $this->translations = new ArrayCollection();
|
|
|
+ }
|
|
|
+
|
|
|
+ public function getTranslations()
|
|
|
+ {
|
|
|
+ return $this->translations;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function addTranslation(CategoryTranslation $t)
|
|
|
+ {
|
|
|
+ if (!$this->translations->contains($t)) {
|
|
|
+ $this->translations[] = $t;
|
|
|
+ $t->setObject($this);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function getId()
|
|
|
+ {
|
|
|
+ return $this->id;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function setTitle($title)
|
|
|
+ {
|
|
|
+ $this->title = $title;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function getTitle()
|
|
|
+ {
|
|
|
+ return $this->title;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function setDescription($description)
|
|
|
+ {
|
|
|
+ $this->description = $description;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function getDescription()
|
|
|
+ {
|
|
|
+ return $this->description;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function __toString()
|
|
|
+ {
|
|
|
+ return $this->getTitle();
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Now the translation entity for the Category:
|
|
|
+
|
|
|
+``` php
|
|
|
+<?php
|
|
|
+namespace Entity;
|
|
|
+
|
|
|
+use Doctrine\ORM\Mapping as ORM;
|
|
|
+use Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @ORM\Entity
|
|
|
+ * @ORM\Table(name="category_translations",
|
|
|
+ * uniqueConstraints={@ORM\UniqueConstraint(name="lookup_unique_idx", columns={
|
|
|
+ * "locale", "object_id", "field"
|
|
|
+ * })}
|
|
|
+ * )
|
|
|
+ */
|
|
|
+class CategoryTranslation extends AbstractPersonalTranslation
|
|
|
+{
|
|
|
+ /**
|
|
|
+ * Convinient constructor
|
|
|
+ *
|
|
|
+ * @param string $locale
|
|
|
+ * @param string $field
|
|
|
+ * @param string $value
|
|
|
+ */
|
|
|
+ public function __construct($locale, $field, $value)
|
|
|
+ {
|
|
|
+ $this->setLocale($locale);
|
|
|
+ $this->setField($field);
|
|
|
+ $this->setContent($value);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ORM\ManyToOne(targetEntity="Category", inversedBy="translations")
|
|
|
+ * @ORM\JoinColumn(name="object_id", referencedColumnName="id", onDelete="CASCADE")
|
|
|
+ */
|
|
|
+ protected $object;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Some example code to persist with translations:
|
|
|
+
|
|
|
+``` php
|
|
|
+// assumes default locale is "en"
|
|
|
+$food = new Entity\Category;
|
|
|
+$food->setTitle('Food');
|
|
|
+$food->addTranslation(new Entity\CategoryTranslation('lt', 'title', 'Maistas'));
|
|
|
+
|
|
|
+$fruits = new Entity\Category;
|
|
|
+$fruits->setParent($food);
|
|
|
+$fruits->setTitle('Fruits');
|
|
|
+$fruits->addTranslation(new Entity\CategoryTranslation('lt', 'title', 'Vaisiai'));
|
|
|
+$fruits->addTranslation(new Entity\CategoryTranslation('ru', 'title', 'rus trans'));
|
|
|
+
|
|
|
+$em->persist($food);
|
|
|
+$em->persist($fruits);
|
|
|
+$em->flush();
|
|
|
+```
|
|
|
+
|
|
|
+This would create translations for english and lithuanian, and for fruits, **ru** additionally.
|
|
|
+
|
|
|
Easy like that, any suggestions on improvements are very welcome
|