Custom Repositories in ORM Doctrine 2

In most cases, the standard methods generated by the Yaml-based doctrine (XML or annotations) are only enough to get some fields using some simple filter. For a more complex query, you have to use the native QueryBuilder and access our model through dql queries. All this is a consequence of a heap of large pieces of code that have the ability to duplicate where identical requests are required. And how would you like to handle the model simply and beautifully through one single method? How? Let's write your own!

Custom repositories


Custom repositories will help us! In short, the meaning of these repositories is to store custom (non-standard) methods for working with models. All methods will be available through the EntityManager for each model to which a specific repository will be “screwed”.

Repositories themselves are descendants of the EntityRepository class. It is better to store them in a separate folder (for example Repositories) and in your namespace (namespace) - let the namespace also be Repositories.

An example of a simple user repository framework:
namespace Repositories;
use Doctrine\ORM\EntityRepository;
use Entities;
class UserRepository extends EntityRepository {
    public function getUserMethod($params)    {    }
}



Project structure


To make it more clear, define the folder structure of our project directory:
  • / bin / - a script for working with the doctrine through BASH
  • / doctrine / - the doctrine itself (installed via GIT)
  • / Entities / - our models with annotations (Created on the basis of YAML by the doctrine itself)
  • / Proxies / - proxies for the Doctrine
  • / public_html / - user part of the project
  • / Repositories / - classes of our repositories (here we will add them)
  • / yaml / - yaml mappers of our models


Doctrine Connection


To begin with, so that the doctrine begins to see our classes, we will register another ClassLoader in the Doctrine general configuration file. In this case, the configuration is in the cli-config.php file . Add the following lines of code to it:
// Константы для путей
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();
...


Connecting the repository to the model


The place of association of the model with the repository depends on what type of mappers we use (Annotations, YAML or XML). Since we use YAML, we connect the repository through it using the attribute repositoryClass where we describe the name of the class and its namespace:
Entities\ModelName:
  type: entity
  repositoryClass: Repositories\UserRepository
  table: model_table
  fields:
    ...


In this example, it is assumed that we have a generated model with anthologies for this 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
{
    ...
}


That's all. Now you can write your own methods in the repository and work with them through our models. Everything turned out to be simple.

Example


A small life example based on writing your own method to a user mood model.
/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

Here it is worth paying attention to how EntityManager ($ em) will be declared in our method . It is done like this:

$this->_em


And now the code of our repository itself:

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();
    }
}


That's all! Now, if we turn to our model, we can work with our method! For example, like this:

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);


Sources



PS

Since I am just starting to actively use ORM in PHP and Doctrine in particular, I can not claim to be a 100% correct solution to this problem in this material, so sound criticism is very welcome! Thanks to all!

Also popular now: