📜 ⬆️ ⬇️

Doctrine ORM behaviors, or how to effectively use traits

Some time has passed since the release of php 5.4, and we decided to experiment with traits, to evaluate their practical application. How can they be used with Doctrine2 objects?

Treit


Trap in php is a set of properties and methods that can be added to a class.
They are implemented at the interpreter level and are completely transparent to Doctrine.

Traits are designed for horizontal reuse and are ideal for adding common behaviors to multiple objects.

General behavior


Often we need to save the time to create and update objects using the created_at and updated_at attributes. This behavior can be applied to any type of object. In such situations, we ask ourselves: "How can I avoid repeating the code every time?"
')

Timestampable behavior


Timestampable is a simple trait that can be applied to Doctrine objects:

<?php use Doctrine\ORM\Mapping as ORM; use Knp\DoctrineBehaviors\ORM as ORMBehaviors; /** * @ORM\Entity */ class Category { use ORMBehaviors\Timestampable\Timestampable; /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="NONE") */ protected $id; } 

Notice the use statement inside the class.

This code will add two properties with a DateTime Doctrine type and two public methods to get the createdAt and updatedAt values :

 <?php $category = new Category; $entityManager->persist($category); $category->getCreatedAt(); $category->getUpdatedAt(); 

After changing the object, getUpdatedAt will return the date of its last update.

Installation


Timestampable and other traits are gathered together in the KnithLabs / DoctrineBehaviours github repository .

You can easily install them using composer. Add these lines to the composer.json file in the root folder of your project.

 { "require": { "knplabs/doctrine-behaviors": "dev-master", } } 

Then run composer:

 curl -s http://getcomposer.org/installer | php php composer.phar install 

Listeners


All this is possible thanks to Doctrine listeners that expect persist or update events for all objects that are Timestampable .

But in order for everything to work, you need to register them. If you use symfony2, it will be easy! Import the service definition file.

 # app/config/config.yml imports: - { resource: ../../vendor/knplabs/doctrine-behaviors/config/orm-services.yml } 

Translatable behavior


Very often, our applications use objects whose properties should be available in several languages. We tried to make the trait for such cases as simple as possible.

In order to obtain an object whose properties can be translated, you must perform 2 steps.

1. Use the traable translatable

 <?php use Doctrine\ORM\Mapping as ORM; use Knp\DoctrineBehaviors\ORM as ORMBehaviors; /** * @ORM\Entity */ class Category { use ORMBehaviors\Translatable\Translatable; /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="NONE") */ protected $id; } 

2. Create a Category Translation object that will use the translation treit:

 <?php use Doctrine\ORM\Mapping as ORM; use Knp\DoctrineBehaviors\ORM as ORMBehaviors; /** * @ORM\Entity */ class CategoryTranslation { use ORMBehaviors\Translatable\Translation; /** * @ORM\Column(type="string") */ protected $name; } 

That's all! TranslatableListener will automatically determine the connection between these two objects. You can work with them as you would with regular OneToMany connections (for example, you can use left join to get translations).

 <?php $category = new Category; $category->translate('ru')->setName(''); $category->translate('en')->setName('Shoes'); $em->persist($category); $category->translate('en')->getName(); 

Tree


Tree uses the implementation of the materialized path to work with trees. All nodes contain their full path from the root:

  | id | name | path | +-----+------------+------------+ | 1 | fr | /1 | | 2 | villes | /1/2 | | 4 | subNantes | /1/2/3/4 | | 7 | en | /7 | | 8 | villes | /7/8 | | 9 | Nantes | /7/8/9 | | 10 | subNantes | /7/8/9/10 | | 11 | Lorient | /7/8/11 | | 12 | Rouen | /7/8/12 | | 6 | Rouen | /1/2/6 | | 3 | Nantes | /1/2/3 | | 5 | Lorient | /1/2/5 | 

Tree \ Node trait is used to represent your objects as a tree

 <?php use Doctrine\ORM\Mapping as ORM; use Knp\DoctrineBehaviors\ORM as ORMBehaviors; /** * @ORM\Entity(repositoryClass="CategoryRepository") */ class Category { use ORMBehaviors\Tree\Node; /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="NONE") */ protected $id; } 

It is also necessary to use the Treit Tree \ Tree in the corresponding EntityRepository :

 <?php use Doctrine\ORM\EntityRepository; use Knp\DoctrineBehaviors\ORM as ORMBehaviors; class CategoryRepository extends EntityRepository { use ORMBehaviors\Tree\Tree; } 

The object now provides a set of methods for working with children, parents, etc.

 $root = $em->getRepository('Category')->getTree(); $root->getParentNode(); $root->getChildren(); $root[0][1]; // array access of children $root->isLeafNode(); $root->isRootNode(); 


Conclusion


In this article we reviewed several examples of the practical application of traits. In addition to the described traits, you can find a complete list of them in the README file on github.

Note. This article is the Russian version of an article published in our blog: Doctrine ORM behaviors, or efficiently . The authors of the English version: Konstantin Kudryashov (@everzet), Florian Klein, Leszek Prabucki. Translation: Alexander Torchenko (@torchello).

Source: https://habr.com/ru/post/144802/


All Articles