Browse Source

made personal translation for example

gedi 13 years ago
parent
commit
d5ee9d3932
5 changed files with 298 additions and 67 deletions
  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_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"
 [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**
 **2012-01-04**
 
 
 - Refactored translatable to be able to persist, update many translations
 - 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 
 - 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
 - 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
 - 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:**
 **Portability:**
 
 
@@ -68,17 +73,20 @@ This article will cover the basic installation and functionality of **Translatab
 Content:
 Content:
 
 
 - [Including](#including-extension) the extension
 - [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)
 - Basic usage [examples](#basic-examples)
 - [Persisting](#multi-translations) multiple translations
 - [Persisting](#multi-translations) multiple translations
 - Using ORM query [hint](#orm-query-hint)
 - Using ORM query [hint](#orm-query-hint)
 - Advanced usage [examples](#advanced-examples)
 - 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
 If you using the source from github repository, initial directory structure for
 the extension library should look like this:
 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
 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 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:
 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**
 - **@Gedmo\Mapping\Annotation\Locale or @Gedmo\Mapping\Annotation\Language** this will identify this column as **locale** or **language**
 used to override the global locale
 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
 **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
 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 Gedmo\Mapping\Annotation as Gedmo;
 use Doctrine\ORM\Mapping as ORM;
 use Doctrine\ORM\Mapping as ORM;
 use Gedmo\Translatable\Translatable;
 use Gedmo\Translatable\Translatable;
+
 /**
 /**
  * @ORM\Table(name="articles")
  * @ORM\Table(name="articles")
  * @ORM\Entity
  * @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
 <?php
 <?php
@@ -238,6 +253,7 @@ namespace Document;
 use Gedmo\Mapping\Annotation as Gedmo;
 use Gedmo\Mapping\Annotation as Gedmo;
 use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
 use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
 use Gedmo\Translatable\Translatable;
 use Gedmo\Translatable\Translatable;
+
 /**
 /**
  * @ODM\Document(collection="articles")
  * @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**
 Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml**
 
 
@@ -328,7 +346,9 @@ Entity\Article:
         - translatable
         - translatable
 ```
 ```
 
 
-## Xml mapping example {#xml}
+<a name="xml-mapping"></a>
+
+## Xml mapping example
 
 
 ``` xml
 ``` xml
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml version="1.0" encoding="UTF-8"?>
@@ -355,6 +375,8 @@ Entity\Article:
 </doctrine-mapping>
 </doctrine-mapping>
 ```
 ```
 
 
+<a name="basic-examples"></a>
+
 ## Basic usage examples: {#basic-examples}
 ## Basic usage examples: {#basic-examples}
 
 
 Currently a global locale used for translations is "en_us" which was
 Currently a global locale used for translations is "en_us" which was
@@ -427,7 +449,9 @@ echo $article->getContent();
 // prints: "my content in en"
 // 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
 Usually it is more convinient to persist more translations when creating
 or updating a record. **Translatable** allows to do that through translation repository.
 or updating a record. **Translatable** allows to do that through translation repository.
@@ -471,7 +495,9 @@ $repo
 $em->flush();
 $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**
 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,
 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.
 **Note:** all these query hints lasts only for the specific query.
 
 
-## Advanced examples: {#advanced-examples}
+<a name="advanced-examples"></a>
+
+## Advanced examples:
 
 
 ### Default locale
 ### Default locale
 
 
@@ -672,4 +700,175 @@ class Article
 
 
 Now all translations of Article will be stored and queried from specific table
 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
 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")
  * @Gedmo\Tree(type="nested")
  * @ORM\Table(name="ext_categories")
  * @ORM\Table(name="ext_categories")
  * @ORM\Entity(repositoryClass="Entity\Repository\CategoryRepository")
  * @ORM\Entity(repositoryClass="Entity\Repository\CategoryRepository")
+ * @Gedmo\TranslationEntity(class="Entity\CategoryTranslation")
  */
  */
 class Category
 class Category
 {
 {
@@ -21,16 +22,22 @@ class Category
 
 
     /**
     /**
      * @Gedmo\Translatable
      * @Gedmo\Translatable
-     * @Gedmo\Slug(fields={"title"})
-     * @ORM\Column(length=64, unique=true)
+     * @ORM\Column(length=64)
      */
      */
-    private $slug;
+    private $title;
 
 
     /**
     /**
      * @Gedmo\Translatable
      * @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
      * @Gedmo\TreeLeft
@@ -68,12 +75,6 @@ class Category
      */
      */
     private $children;
     private $children;
 
 
-    /**
-     * @Gedmo\Translatable
-     * @ORM\Column(type="text", nullable=true)
-     */
-    private $description;
-
     /**
     /**
      * @Gedmo\Timestampable(on="create")
      * @Gedmo\Timestampable(on="create")
      * @ORM\Column(type="datetime")
      * @ORM\Column(type="datetime")
@@ -87,14 +88,31 @@ class Category
     private $updated;
     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()
     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()
     public function getSlug()
@@ -119,7 +137,7 @@ class Category
 
 
     public function setDescription($description)
     public function setDescription($description)
     {
     {
-        $this->description = strip_tags($description);
+        $this->description = $description;
     }
     }
 
 
     public function getDescription()
     public function getDescription()
@@ -172,11 +190,6 @@ class Category
         return $this->updated;
         return $this->updated;
     }
     }
 
 
-    public function setTranslatableLocale($locale)
-    {
-        $this->locale = $locale;
-    }
-
     public function __toString()
     public function __toString()
     {
     {
         return $this->getTitle();
         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
 // standard annotation reader
 $annotationReader = new Doctrine\Common\Annotations\AnnotationReader;
 $annotationReader = new Doctrine\Common\Annotations\AnnotationReader;
-// gedmo annotation loader
-Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace(
-    'Gedmo\Mapping\Annotation',
-    $gedmoPath
-);
 // standard doctrine annotations
 // standard doctrine annotations
 Doctrine\Common\Annotations\AnnotationRegistry::registerFile(
 Doctrine\Common\Annotations\AnnotationRegistry::registerFile(
     $vendorPath.'/doctrine-orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php'
     $vendorPath.'/doctrine-orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php'
 );
 );
 // register annotation driver
 // register annotation driver
 $driverChain = new Doctrine\ORM\Mapping\Driver\DriverChain();
 $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(
 $annotationDriver = new Doctrine\ORM\Mapping\Driver\AnnotationDriver($annotationReader, array(
     __DIR__.'/app/Entity', // example entity
     __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, 'Entity');
-$driverChain->addDriver($annotationDriver, 'Gedmo\Translatable\Entity');
-$driverChain->addDriver($annotationDriver, 'Gedmo\Loggable\Entity');
-$driverChain->addDriver($annotationDriver, 'Gedmo\Tree\Entity');
 
 
 // register metadata driver
 // register metadata driver
 $config->setMetadataDriverImpl($driverChain);
 $config->setMetadataDriverImpl($driverChain);
@@ -93,7 +86,7 @@ $evm->addEventSubscriber(new Gedmo\Sluggable\SluggableListener);
 $evm->addEventSubscriber(new Gedmo\Tree\TreeListener);
 $evm->addEventSubscriber(new Gedmo\Tree\TreeListener);
 $evm->addEventSubscriber(new Gedmo\Loggable\LoggableListener);
 $evm->addEventSubscriber(new Gedmo\Loggable\LoggableListener);
 $evm->addEventSubscriber(new Gedmo\Timestampable\TimestampableListener);
 $evm->addEventSubscriber(new Gedmo\Timestampable\TimestampableListener);
-$translatable = new Gedmo\Translatable\TranslationListener;
+$translatable = new Gedmo\Translatable\TranslatableListener;
 $translatable->setTranslatableLocale('en');
 $translatable->setTranslatableLocale('en');
 $translatable->setDefaultLocale('en');
 $translatable->setDefaultLocale('en');
 $evm->addEventSubscriber($translatable);
 $evm->addEventSubscriber($translatable);

+ 6 - 17
example/run.php

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