
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 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:
To make it more clear, define the folder structure of our project directory:
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:
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:
In this example, it is assumed that we have a generated model with anthologies for this YAML.
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.
A small life example based on writing your own method to a user mood model.
/Entities/UserMood.php
/yaml/Entities.UserMood.dcm.yml
/Repositories/RUserMood.php
Here it is worth paying attention to how EntityManager ($ em) will be declared in our method . It is done like this:
And now the code of our repository itself:
That's all! Now, if we turn to our model, we can work with our method! For example, like this:
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!
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
- Original ORM Doctrine 2.1 Documentation
- http://mackstar.com/blog/2010/10/04/using-repositories-doctrine-2
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!