Pull to refresh

Doctrine ORM behaviors, или как эффективно использовать трейты

Reading time4 min
Views7.1K
С момента релиза php 5.4 уже прошло некоторое время, и мы решили поэкспериментировать с трейтами (traits), оценить их практическое применение. Как их можно использовать вместе с объектами Doctrine2?

Трейты


Трейты в php — это набор свойств и методов, которые можно добавить в класс.
Они реализованы на уровне интерпретатора и являются абсолютно прозрачными для Doctrine.

Трейты спроектированы для горизонтального повторного использования и идеально подходят для добавления общего поведения в несколько объектов.

Общее поведение


Часто нам необходимо сохранять время создания и обновления объектов, используя атрибуты created_at и updated_at. Этo поведение может быть применено к любым типам объектов. В таких ситуациях мы спрашиваем себя: «Как мне избежать повторения кодa каждый раз?».

Timestampable behavior


Timestampable — это простой трейт, который можно применить к объектам Doctrine:

<?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;
}

Обратите внимание на оператор use внутри класса.

Этот код добавит два свойства с Doctrine типом DateTime и два публичных метода для получения значений createdAt и updatedAt:

<?php

$category = new Category;
$entityManager->persist($category);

$category->getCreatedAt();
$category->getUpdatedAt();

После изменения объекта getUpdatedAt вернет дату его последнего обновления.

Установка


Timestampable и другие трейты собраны вместе в github репозитории KnpLabs/DoctrineBehaviours.

Вы можете легко установить их, используя composer. Добавьте эти строки в файл composer.json в корневой папке вашего проекта.

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

Затем запустите composer:

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

Listeners


Все это возможно благодаря Doctrine listeners, которые ожидают события persist или update для всех объектов, которые используют Timestampable.

Но для того, чтобы все заработало, необходимо их зарегистрировать. Если вы используете Symfony2, это будет просто! Импортируйте файл с определениями сервисов.

# app/config/config.yml

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

Translatable behavior


Очень часто наши приложения используют объекты, свойства которых должны быть доступны на нескольких языках. Мы постарались сделать трейт для таких случаев максимально простым.

Для того, чтобы получить объект, свойства которого можно переводить, необходимо выполнить 2 шага.

1. Используйте трейт 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. Создайте объект CategoryTranslation, которая будет использовать трейт Translation:

<?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;
}

Вот и всё! TranslatableListener определит связь между этими двумя объектами автоматически. Вы можете работать с ними также, как с обычными связями OneToMany (например, вы можете использовать left join для получения переводов).

<?php

$category = new Category;
$category->translate('ru')->setName('Обувь');
$category->translate('en')->setName('Shoes');
$em->persist($category);

$category->translate('en')->getName();

Tree


Tree использует реализацию materialized path для работы с деревьями. Все ноды содержат свой полный путь от корня:

 | 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

<?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;
}

Также необходимо использовать трейт Tree\Tree в соответствующем EntityRepository:

<?php

use Doctrine\ORM\EntityRepository;

use Knp\DoctrineBehaviors\ORM as ORMBehaviors;

class CategoryRepository extends EntityRepository
{
    use ORMBehaviors\Tree\Tree;
}

Теперь объект предоставляет набор методов для работы с дочерними объектами, родителями и т.д.

$root = $em->getRepository('Category')->getTree();

$root->getParentNode();
$root->getChildren();
$root[0][1]; // array access of children
$root->isLeafNode();
$root->isRootNode();


Заключение


В данной статье мы рассмотрели несколько примеров практического применения трейтов. Помимо описанных трейтов вы можете ознакомиться с полным их перечнем в README-файле на github.

Примечание. Данная статья является русской версией статьи, опубликованной в нашем блоге: Doctrine ORM behaviors, or how to use traits efficiently. Авторы английской версии: Константин Кудряшов (@everzet), Florian Klein, Leszek Prabucki. Перевод: Александр Торченко (@torchello).
Tags:
Hubs:
+20
Comments17

Articles

Change theme settings