В большинстве случаев стандартные методы, генерируемые доктриной на основе Yaml (XML или аннотаций), хватает только на получение каких то полей по какому-то простому фильтру. Для более сложного запроса приходиться пользоваться нативным QueryBuilder'ом и обращаться через dql запросы к нашей модели. Все это является следствием нагромождения больших кусков кода, которые имеют свойства дублироваться там где требуется применить идентичные запросы. А как хотелось бы обращаться с моделью просто и красиво через один единственный метод? Как? Напишем свой!
Нам помогут пользовательские репозитории! Если коротко, то смысл этих репозиториев в том, что бы хранить в себе пользовательские (нестандартные) методы для работы с моделями. Все методы будут доступны через EntityManager для каждой модели, к которой будет «прикручен» конкретный репозиторий.
Сами репозитории являются наследниками класса EntityRepository. Их лучше хранить в отдельной папке (например Repositories) и в своем пространстве имен (namespace) — пусть неймспейс будет также Repositories.
Пример каркаса простого пользовательского репозитория:
Что бы было более понятно, определим структуру папок каталога нашего проекта:
Для начала, что бы доктрина начала видеть наши классы, мы в файле общей конфигурации Doctrine зарегистрируем еще один ClassLoader. В данном случае конфигурация лежит в файле cli-config.php. Добавляем в него следующие строчки кода:
Место ассоциации модели с репозиторием зависит от того, какой тип мапперов мы используем (Аннтоации, YAML или XML). Поскольку у нас используется YAML, то подключаем репозиторий через него, используя атрибут repositoryClass где мы описываем название класса и его пространство имен:
В данном примере подразумевается, что у нас есть сгенерированная модель с аннтоциями для данного YAML.
Вот собственно и все. теперь можно писать собственные методы в репозитории и работать с ними через наши модели. Все оказалось просто.
Небольшой жизненный пример, основанный на написании собственного метода к модели пользовательского настроение.
/Entities/UserMood.php
/yaml/Entities.UserMood.dcm.yml
/Repositories/RUserMood.php
Тут стоит обратить внимание на то, как в нашем методе будет объявляется EntityManager ($em). Делается это так:
А теперь сам код нашего репозитория:
Вот и все! Теперь если мы обратимся к нашей модели то сможем работать с нашим методом! Например так:
Поскольку я только начинаю активно использовать ORM на PHP и Doctrine в частности, то я не могу претендовать на 100% правильное решение данной задачи в данном материале, по этому здравая критика очень приветствуется! Всем спасибо!
Пользовательские репозитории
Нам помогут пользовательские репозитории! Если коротко, то смысл этих репозиториев в том, что бы хранить в себе пользовательские (нестандартные) методы для работы с моделями. Все методы будут доступны через EntityManager для каждой модели, к которой будет «прикручен» конкретный репозиторий.
Сами репозитории являются наследниками класса EntityRepository. Их лучше хранить в отдельной папке (например Repositories) и в своем пространстве имен (namespace) — пусть неймспейс будет также Repositories.
Пример каркаса простого пользовательского репозитория:
namespace Repositories; use Doctrine\ORM\EntityRepository; use Entities; class UserRepository extends EntityRepository { public function getUserMethod($params) { } }
Структура проекта
Что бы было более понятно, определим структуру папок каталога нашего проекта:
- /bin/ — скрипт для работы с доктриной через BASH
- /doctrine/ — сама доктрина (установленная через GIT)
- /Entities/ — наши модели с аннотациями (Созданные на основе YAML самой доктриной)
- /Proxies/ — прокси для Доктрины
- /public_html/ — пользовательская часть проекта
- /Repositories/ — классы наших репозиториев (сюда мы их будем складывать)
- /yaml/ — yaml мапперы наших моделей
Подключание к доктрине
Для начала, что бы доктрина начала видеть наши классы, мы в файле общей конфигурации Doctrine зарегистрируем еще один ClassLoader. В данном случае конфигурация лежит в файле cli-config.php. Добавляем в него следующие строчки кода:
// Константы для путей define("ROOT_DIR", __DIR__); define("DOCTRINE_DIR", ROOT_DIR."/doctrine"); ... // Если не заинклуден сам класслоадер, то инклудим сначала его require_once DOCTRINE_DIR . '/lib/vendor/doctrine-common/lib/Doctrine/Common/ClassLoader.php'; // Теперь среди прочего регистрируем в класслоадере наши классы для репозитория $classLoader = new ClassLoader('Repositories', ROOT_DIR); $classLoader->register(); ...
Подключение репозитория к модели
Место ассоциации модели с репозиторием зависит от того, какой тип мапперов мы используем (Аннтоации, YAML или XML). Поскольку у нас используется YAML, то подключаем репозиторий через него, используя атрибут repositoryClass где мы описываем название класса и его пространство имен:
Entities\ModelName: type: entity repositoryClass: Repositories\UserRepository table: model_table fields: ...
В данном примере подразумевается, что у нас есть сгенерированная модель с аннтоциями для данного YAML.
namespace Entities; use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\EntityRepository; /** * Entities\ModelName * * @Table(name="model_table") * @Entity(repositoryClass="Repositories\UserRepository") */ class ModelName { ... }
Вот собственно и все. теперь можно писать собственные методы в репозитории и работать с ними через наши модели. Все оказалось просто.
Пример
Небольшой жизненный пример, основанный на написании собственного метода к модели пользовательского настроение.
/Entities/UserMood.php
namespace Entities; use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\EntityRepository; /** * Entities\UserMood * * @Table(name="b_mood") * @Entity(repositoryClass="Repositories\RUserMood") */ class UserMood { /** * @var integer $ID * * @Column(name="ID", type="integer") * @Id * @GeneratedValue(strategy="IDENTITY") */ private $ID; /** * @var string $ACTIVE * * @Column(name="ACTIVE", type="string", length=1, nullable=false) */ private $ACTIVE; /** * @var string $NAME * * @Column(name="NAME", type="string", length=255, nullable=false) */ private $NAME; /** * @var datetime $DATE_ACTIVE_FROM * * @Column(name="DATE_ACTIVE_FROM", type="datetime", nullable=false) */ private $DATE_ACTIVE_FROM; /** * @var decimal $VALUE * * @Column(name="VALUE", type="decimal", nullable=false) */ private $VALUE; /** * @var integer $USER_ID * * @Column(name="USER_ID", type="integer", length=6, nullable=false) */ private $USER_ID; /** * @var string $UNAUTH_HASH * * @Column(name="UNAUTH_HASH", type="string", length=255, nullable=false) */ private $UNAUTH_HASH; //..... тут идут методы для работы с полями, которые я публиковать не буду для экномии места. //..... методы созданы самой ORM. }
/yaml/Entities.UserMood.dcm.yml
Entities\UserMood: type: entity repositoryClass: Repositories\RUserMood table: b_mood fields: ID: id: true type: integer generator: strategy: AUTO ACTIVE: type: string length: 1 nullable: false NAME: type: string length: 255 nullable: false DATE_ACTIVE_FROM: type: datetime nullable: false VALUE: type: decimal nullable: false USER_ID: type: integer length: 6 nullable: false UNAUTH_HASH: type: string length: 255 nullable: false
/Repositories/RUserMood.php
Тут стоит обратить внимание на то, как в нашем методе будет объявляется EntityManager ($em). Делается это так:
$this->_em
А теперь сам код нашего репозитория:
namespace Repositories; use Doctrine\ORM\EntityRepository; use Entities; class RUserMood extends EntityRepository { public function getMoodsInDateRange($from, $to, $user = false) { $qb = $this->_em->createQueryBuilder(); $filter[1] = $from; $filter[2] = $to; if (!$user) { $varwhere = $qb->expr()->andX( $qb->expr()->gte('um.DATE_ACTIVE_FROM', '?1'), $qb->expr()->lte('um.DATE_ACTIVE_FROM', '?2') ); } else { $filter[3] = $user; $varwhere = $qb->expr()->andX( $qb->expr()->gte('um.DATE_ACTIVE_FROM', '?1'), $qb->expr()->lte('um.DATE_ACTIVE_FROM', '?2'), $qb->expr()->eq('um.USER_ID', '?3') ); } $qb->add('select', new \Doctrine\ORM\Query\Expr\Select(array('um'))) ->add('from', new \Doctrine\ORM\Query\Expr\From('Entities\UserMood', 'um')) ->add('where', $varwhere) ->add('orderBy', new \Doctrine\ORM\Query\Expr\OrderBy('um.DATE_ACTIVE_FROM', 'DESC')) ->setParameters($filter); return $qb->getQuery(); } }
Вот и все! Теперь если мы обратимся к нашей модели то сможем работать с нашим методом! Например так:
include_once ('../cli-config.php'); $repo = DDB::getEM()->getRepository('Entities\UserMood')->getMoodsInDateRange('2011-05-01 00:00:00', '2011-05-09 23:59:59')->getResult(); print_r($repo);
Источники
- Оригинальная документация по ORM Doctrine 2.1
- http://mackstar.com/blog/2010/10/04/using-repositories-doctrine-2
P.S.
Поскольку я только начинаю активно использовать ORM на PHP и Doctrine в частности, то я не могу претендовать на 100% правильное решение данной задачи в данном материале, по этому здравая критика очень приветствуется! Всем спасибо!
