Doctrine入門、QueryBuilder

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()でselectfrom等を追加していき、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()を使います。
またASCDESCDoctrine\Common\Collections\Criteria::ASCDoctrine\Common\Collections\Criteria::DESCのようにあらかじめ定義されています。

BlockEditor certificate css DataGrid Docker Gutenberg Hyper-V iframe MUI openssl PHP React ReduxToolkit REST ubuntu WordPress オレオレ認証局 フレームワーク