瀏覽代碼

made personal translation for example

gedi 13 年之前
父節點
當前提交
d5ee9d3932
共有 5 個文件被更改,包括 298 次插入67 次删除
  1. 215 16
      doc/translatable.md
  2. 34 21
      example/app/Entity/Category.php
  3. 37 0
      example/app/Entity/CategoryTranslation.php
  4. 6 13
      example/em.php
  5. 6 17
      example/run.php

+ 215 - 16
doc/translatable.md

@@ -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

+ 34 - 21
example/app/Entity/Category.php

@@ -9,6 +9,7 @@ use Doctrine\ORM\Mapping as ORM;
  * @Gedmo\Tree(type="nested")
  * @ORM\Table(name="ext_categories")
  * @ORM\Entity(repositoryClass="Entity\Repository\CategoryRepository")
+ * @Gedmo\TranslationEntity(class="Entity\CategoryTranslation")
  */
 class Category
 {
@@ -21,16 +22,22 @@ class Category
 
     /**
      * @Gedmo\Translatable
-     * @Gedmo\Slug(fields={"title"})
-     * @ORM\Column(length=64, unique=true)
+     * @ORM\Column(length=64)
      */
-    private $slug;
+    private $title;
 
     /**
      * @Gedmo\Translatable
-     * @ORM\Column(length=64)
+     * @ORM\Column(type="text", nullable=true)
      */
-    private $title;
+    private $description;
+
+    /**
+     * @Gedmo\Translatable
+     * @Gedmo\Slug(fields={"title"})
+     * @ORM\Column(length=64, unique=true)
+     */
+    private $slug;
 
     /**
      * @Gedmo\TreeLeft
@@ -68,12 +75,6 @@ class Category
      */
     private $children;
 
-    /**
-     * @Gedmo\Translatable
-     * @ORM\Column(type="text", nullable=true)
-     */
-    private $description;
-
     /**
      * @Gedmo\Timestampable(on="create")
      * @ORM\Column(type="datetime")
@@ -87,14 +88,31 @@ class Category
     private $updated;
 
     /**
-     * Used locale to override Translation listener`s locale
-     * @Gedmo\Locale
+     * @ORM\OneToMany(
+     *   targetEntity="CategoryTranslation",
+     *   mappedBy="object",
+     *   cascade={"persist", "remove"}
+     * )
      */
-    private $locale;
+    private $translations;
 
     public function __construct()
     {
-    	$this->children = new ArrayCollection();
+        $this->children = new ArrayCollection();
+        $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 getSlug()
@@ -119,7 +137,7 @@ class Category
 
     public function setDescription($description)
     {
-        $this->description = strip_tags($description);
+        $this->description = $description;
     }
 
     public function getDescription()
@@ -172,11 +190,6 @@ class Category
         return $this->updated;
     }
 
-    public function setTranslatableLocale($locale)
-    {
-        $this->locale = $locale;
-    }
-
     public function __toString()
     {
         return $this->getTitle();

+ 37 - 0
example/app/Entity/CategoryTranslation.php

@@ -0,0 +1,37 @@
+<?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;
+}

+ 6 - 13
example/em.php

@@ -57,29 +57,22 @@ $config->setAutoGenerateProxyClasses(false);
 
 // standard annotation reader
 $annotationReader = new Doctrine\Common\Annotations\AnnotationReader;
-// gedmo annotation loader
-Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace(
-    'Gedmo\Mapping\Annotation',
-    $gedmoPath
-);
 // standard doctrine annotations
 Doctrine\Common\Annotations\AnnotationRegistry::registerFile(
     $vendorPath.'/doctrine-orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php'
 );
 // register annotation driver
 $driverChain = new Doctrine\ORM\Mapping\Driver\DriverChain();
+// load superclass extension metadata mapping into driver chain also registers annotation autoload
+Gedmo\DoctrineExtensions::registerAbstractMappingIntoDriverChainORM($driverChain);
+
+// personal annotation reader for Entity namespace
 $annotationDriver = new Doctrine\ORM\Mapping\Driver\AnnotationDriver($annotationReader, array(
     __DIR__.'/app/Entity', // example entity
-    $gedmoPath.'/Gedmo/Translatable/Entity',
-    $gedmoPath.'/Gedmo/Loggable/Entity',
-    $gedmoPath.'/Gedmo/Tree/Entity',
 ));
 
-// drivers for diferent namespaces
+// register our Entity driver
 $driverChain->addDriver($annotationDriver, 'Entity');
-$driverChain->addDriver($annotationDriver, 'Gedmo\Translatable\Entity');
-$driverChain->addDriver($annotationDriver, 'Gedmo\Loggable\Entity');
-$driverChain->addDriver($annotationDriver, 'Gedmo\Tree\Entity');
 
 // register metadata driver
 $config->setMetadataDriverImpl($driverChain);
@@ -93,7 +86,7 @@ $evm->addEventSubscriber(new Gedmo\Sluggable\SluggableListener);
 $evm->addEventSubscriber(new Gedmo\Tree\TreeListener);
 $evm->addEventSubscriber(new Gedmo\Loggable\LoggableListener);
 $evm->addEventSubscriber(new Gedmo\Timestampable\TimestampableListener);
-$translatable = new Gedmo\Translatable\TranslationListener;
+$translatable = new Gedmo\Translatable\TranslatableListener;
 $translatable->setTranslatableLocale('en');
 $translatable->setDefaultLocale('en');
 $evm->addEventSubscriber($translatable);

+ 6 - 17
example/run.php

@@ -1,7 +1,7 @@
 <?php
 
 use Doctrine\ORM\Query;
-use Gedmo\Translatable\TranslationListener;
+use Gedmo\Translatable\TranslatableListener;
 
 $executionStart = microtime(true);
 $memoryStart = memory_get_usage(true);
@@ -21,39 +21,28 @@ if (!$food) {
     // lets create some categories
     $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'));
 
     $apple = new Entity\Category;
     $apple->setParent($fruits);
     $apple->setTitle('Apple');
+    $apple->addTranslation(new Entity\CategoryTranslation('lt', 'title', 'Obuolys'));
 
     $milk = new Entity\Category;
     $milk->setParent($food);
     $milk->setTitle('Milk');
+    $milk->addTranslation(new Entity\CategoryTranslation('lt', 'title', 'Pienas'));
 
     $em->persist($food);
     $em->persist($milk);
     $em->persist($fruits);
     $em->persist($apple);
     $em->flush();
-
-    // translate into LT
-    $translatable->setTranslatableLocale('lt');
-    $food->setTitle('Maistas');
-    $fruits->setTitle('Vaisiai');
-    $apple->setTitle('Obuolys');
-    $milk->setTitle('Pienas');
-
-    $em->persist($food);
-    $em->persist($milk);
-    $em->persist($fruits);
-    $em->persist($apple);
-    $em->flush();
-    // set locale back to en
-    $translatable->setTranslatableLocale('en');
 }
 
 // create query to fetch tree nodes
@@ -82,7 +71,7 @@ $treeDecorationOptions = array(
 // build tree in english
 echo $repository->buildTree($query->getArrayResult(), $treeDecorationOptions).PHP_EOL.PHP_EOL;
 // change locale
-$query->setHint(TranslationListener::HINT_TRANSLATABLE_LOCALE, 'lt');
+$query->setHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE, 'lt');
 // build tree in lithuanian
 echo $repository->buildTree($query->getArrayResult(), $treeDecorationOptions).PHP_EOL.PHP_EOL;