DoctrineにはDQLを組み立てるAPIが用意されています。
その名もまんまQueryBuilder
ですが、今回はこれを使っていきます。
use Doctrine\ORM\EntityManager;
/** @var EntityManager $manager */
$manager = require 'createManager.php';
$builder = $manager->createQueryBuilder();
$builder
->add('select', 'i')
->add('from', 'App\Entities\Item i')
->add('where', 'i.power > :p')
->setParameter(':p', 3);
$query = $builder->getQuery();
echo sprintf("DQL: %s\n", $query->getDQL());
echo sprintf("SQL: %s\n", $query->getSQL());
$items = $query->getResult();
foreach($items as $item)
{
echo "{$item->getId()} - {$item->getName()} : {$item->getPower()}\n";
}
DQL: SELECT i FROM App\Entities\Item i WHERE i.power > :p
SQL: SELECT i0_.id AS id_0, i0_.name AS name_1, i0_.power AS power_2 FROM Item i0_ WHERE i0_.power > ?
1 - F : 8
3 - C : 6
4 - D : 5
5 - G : 7
7 - B : 4
まずはEntityManager
からcreateQueryBuilder()
を使ってQueryBuilder
の作成です。
$builder = $manager->createQueryBuilder();
今回使うadd()
メソッドは低レベルAPIなので直接使うことはあまりないようですが学習用として使います。
$builder->add('select', 'i')->add('from', 'App\Entities\Item i')->add('where', 'i.power > :p')->setParameter(':p', 3);
add()メソッドは自身のインスタンス(QueryBuilder
)を返すのでメソッドチェーンで繋げます。
add()でselect
やfrom
等を追加していき、QueryBuilderからgetQuery()
でQuery
のインスタンスを取得できます。
$query = $builder->getQuery();
ビルダから作成されたクエリのDQLが次です。
SELECT i FROM App\Entities\Item i WHERE i.power > :p
ビルダから複数のクエリインスタンス
getQuery()から返されるクエリのインスタンスは毎回違うようです。
use Doctrine\ORM\EntityManager;
/** @var EntityManager $manager */
$manager = require 'createManager.php';
$builder = $manager->createQueryBuilder();
$builder
->add('select', 'i')
->add('from', 'App\Entities\Item i')
->add('where', 'i.power > :p')
->setParameter(':p', 3);
$queryA = $builder->getQuery();
$queryB = $builder->getQuery();
echo $queryA === $queryB ? 'TRUE' : 'FALSE';
FALSE
add()使うよりもそれぞれ句用のメソッドが用意されてます。
普通はこちらを使います。
$builder
->select('i')
->from('App\Entities\Item', 'i')
->where('i.power > :p')
->setParameter(':p', 4);
DQLの結果はさっきと同じです。
式
QueryBuilderのWHERE
句などを組み立てるパーツもあります。
use Doctrine\ORM\EntityManager;
/** @var EntityManager $manager */
$manager = require 'createManager.php';
$builder = $manager->createQueryBuilder();
$ex = $builder->expr();
$eq = $ex->eq('i.power', 1);
$gt = $ex->gt('i.id', 3);
$lt = $ex->lt('i.id', 5);
$and = $ex->andX($eq, $lt, $gt);
$or = $ex->orX($eq, $lt, $gt);
$list = [$eq, $gt, $lt, $and, $or];
foreach($list as $e)
{
echo "$e \n";
}
i.power = 1
i.id > 3
i.id < 5
i.power = 1 AND i.id < 5 AND i.id > 3
i.power = 1 OR i.id < 5 OR i.id > 3
まずはQueryBuilder
からexpr()
でDoctrine\ORM\Query\Expr
を作成します。
$ex = $builder->expr();
eq()
は=
を作成します。
$ex->eq('i.power', 1);
↓
i.power = 1
$ex->gt('i.id', 3);
はi.id > 3
、$ex->lt('i.id', 5);
はi.id < 5
になります。
DQLでAND
を作成するにはandX()
を使います。
引数は複数でもいいですが、
$ex->andX($eq, $lt, $gt);
これまでのeq()
, lt()
, gt()
をまとめてANDで繋げられます。
i.power = 1 AND i.id < 5 AND i.id > 3
これらをうまく組み合わせて条件式を組み立てて行きます。
use Doctrine\ORM\EntityManager;
/** @var EntityManager $manager */
$manager = require 'createManager.php';
$builder = $manager->createQueryBuilder();
$ex = $builder->expr();
$gt = $ex->gt('i.power', ':min');
$lt = $ex->lt('i.power', ':max');
$builder
->select('i')
->from('App\Entities\Item', 'i')
->where($ex->andX($lt, $gt))
->setParameter(':min', 3)
->setParameter(':max', 7);
$query = $builder->getQuery();
echo sprintf("DQL: %s\n", $query->getDQL());
echo sprintf("SQL: %s\n", $query->getSQL());
$items = $query->getResult();
foreach($items as $item)
{
echo "{$item->getId()} - {$item->getName()} : {$item->getPower()}\n";
}
DQL: SELECT i FROM App\Entities\Item i WHERE i.power < :max AND i.power > :min
SQL: SELECT i0_.id AS id_0, i0_.name AS name_1, i0_.power AS power_2 FROM Item i0_ WHERE i0_.power < ? AND i0_.power > ?
3 - C : 6
4 - D : 5
7 - B : 4
それぞれ式は、
$ex->gt('i.power', ':min')
はpower > :min
$ex->lt('i.power', ':max')
はi.power < :max
これらをandX()
の戻り値(i.power < :max AND i.power > :min
)をwhere()
に渡します。
where($ex->andX($lt, $gt))
最終的に出力されるDQLは次のようになります。
SELECT i FROM App\Entities\Item i WHERE i.power < :max AND i.power > :min
他にもいろいろな式がかけますが、その一部で減算のdiff()
や平方根のsqrt()
などを使ってみます。
use Doctrine\ORM\EntityManager;
/** @var EntityManager $manager */
$manager = require 'createManager.php';
$builder = $manager->createQueryBuilder();
$ex = $builder->expr();
$or = $ex->orX(
$ex->gt(
5,
$ex->diff('i.power', 3)
),
$ex->lt(
2,
$ex->sqrt('i.power')
)
);
echo "$or\n";
5 > i.power - 3 OR 2 < SQRT(i.power)
順序
use Doctrine\ORM\EntityManager;
/** @var EntityManager $manager */
$manager = require 'createManager.php';
$builder = $manager->createQueryBuilder();
// $builder->orderBy('i.name', Doctrine\Common\Collections\Criteria::ASC)
$builder
->select('i')
->from('App\Entities\Item', 'i')
->orderBy('i.name', 'ASC');
$items = $builder->getQuery()->getResult();
foreach($items as $item)
{
echo "{$item->getId()} - {$item->getName()} : {$item->getPower()}\n";
}
順序をorderBy()
を使います。
またASC
とDESC
はDoctrine\Common\Collections\Criteria::ASC
やDoctrine\Common\Collections\Criteria::DESC
のようにあらかじめ定義されています。