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

в 14:43, , рубрики: Doctrine ORM, Doctrine2, php, php 5.4, symfony, symfony2, traits, метки: , , ,

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

Трейты

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

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

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

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

Timestampable behavior

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

<?php

use DoctrineORMMapping as ORM;

use KnpDoctrineBehaviorsORM as ORMBehaviors;

/**
 * @ORMEntity
 */
class Category
{
    use ORMBehaviorsTimestampableTimestampable;

    /**
     * @ORMId
     * @ORMColumn(type="integer")
     * @ORMGeneratedValue(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 DoctrineORMMapping as ORM;

use KnpDoctrineBehaviorsORM as ORMBehaviors;

/**
 * @ORMEntity
 */
class Category
{
    use ORMBehaviorsTranslatableTranslatable;

    /**
     * @ORMId
     * @ORMColumn(type="integer")
     * @ORMGeneratedValue(strategy="NONE")
     */
    protected $id;
}

2. Создайте сущность CategoryTranslation, которая будет использовать трейт Translation:

<?php

use DoctrineORMMapping as ORM;

use KnpDoctrineBehaviorsORM as ORMBehaviors;

/**
 * @ORMEntity
 */
class CategoryTranslation
{
    use ORMBehaviorsTranslatableTranslation;

    /**
     * @ORMColumn(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     |

Для представления ваших сущностей в виде дерева использует трейт TreeNode

<?php

use DoctrineORMMapping as ORM;

use KnpDoctrineBehaviorsORM as ORMBehaviors;

/**
 * @ORMEntity(repositoryClass="CategoryRepository")
 */
class Category
{
    use ORMBehaviorsTreeNode;

    /**
     * @ORMId
     * @ORMColumn(type="integer")
     * @ORMGeneratedValue(strategy="NONE")
     */
    protected $id;
}

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

<?php

use DoctrineORMEntityRepository;

use KnpDoctrineBehaviorsORM as ORMBehaviors;

class CategoryRepository extends EntityRepository
{
    use ORMBehaviorsTreeTree;
}

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

$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).

Автор: knplabs


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js