Doctrine2 で DQL を書くのに括弧付きの WHERE 句をどう実現するか?

Doctrine2 で DQL を書くのに括弧付きの WHERE 句をどう実現するかとても悩んでいろいろ検索したけど、日本語での記事をあまり見かけなかったので残しておく。

まず、そもそも Doctrine2 のレポジトリにどうやって DQL を書いたらいいのか悩んだ。「$this->_em」ってのがエンティティマネージャーなんだね。


/**
* ItemRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ItemRepository extends EntityRepository
{
public function searchByKeywords()
{
$qb = $this->_em->createQueryBuilder();
}
}


商品検索用の DQL を書きたかった。まずは簡単にキーワードが商品名に含まれる商品を検索したかったんだけど like をどうしたらいいのか迷った。というのも便利なあいまい検索用のメソッドがあるんじゃないかと思ってた。が、それは期待しすぎ。普通にパーセント(%)を前後につけて対応。


public function searchByKeywords($keywords)
{
$qb = $this->_em->createQueryBuilder();
$qb->select('item')
->from('Entities\Item\Item', 'item')
->where('item.item_name like :keywords')
->setParameter('keywords', '%' . $keywords . '%')
;

return $qb->getQuery()->getResult();
}


で、本題。検索対象を商品名だけでなくていろんな属性に対して横断的にするには or を使うんだけど、「商品の所有者の中で」という条件が付くと MySQL だとこんな感じになる。


SELECT
*
FROM
`items`
WHERE
`owner_id` = 'hoge' AND (
`name` like '%huga%' OR
`description` like '%huga%' OR
`summary` like '%huga%'
)


クエリービルダーの orWhere メソッドとか add メソッドとか使ってみるもうまくいかず。結局は日本語の記事を見つけて解決したんだけどw、andWhere メソッドの第2引数の中に括弧を記述しちゃうって方法。


public function searchByKeywords($owner_id, $keywords)
{
$qb = $this->_em->createQueryBuilder();
$qb->select('item')
->from('Entities\Item\Item', 'item')
->where('item.owner_id = :owner_id')
->setParameter('owner_id', $owner_id)
->andWhere('(
item.name like :keywords or
item.description like :keywords or
item.summary like :keywords
)')
->setParameter('keywords', '%' . $keywords . '%')
;

return $qb->getQuery()->getResult();
}


なんとか意図した動作を実装できた。で、今度は検索機能なので将来的に検索条件が複雑になったときのことを考える。このクエリービルダーはメソッドチェーンになってて、ずらずらずらーっとアローに続けてメソッドを追加していけばいいんだけど、条件によってメソッドを切替えたいときに保守性が低いなと。でこうなりました。


public function searchByKeywords($owner_id, $keywords, $price, $order_by)
{
$order_by = empty($order_by) ? 'DESC' : $order_by;

$qb = $this->_em->createQueryBuilder();
$qb->select('item')
->from('Entities\Item\Item', 'item')
->where('item.owner_id = :owner_id')
->setParameter('owner_id', $owner_id)
;

if ($keywords) {
$qb->andWhere(
'(
item.name like :keywords or
item.description like :keywords or
item.summary like :keywords
)')
->setParameter('keywords', '%' . $keywords . '%')
;
}

if ($price) {
$qb->andWhere('item.price < :price')->setParameter('price', $price);
}

$qb->add('orderBy', 'item.no ' . $order_by);

return $qb->getQuery()->getResult();
}


and 条件を追加していくには andWhere メソッドを必要なだけ記述すればいい。かつて SQL を書いてた頃に比べると格段に保守性が高いなと感じています。今後検索条件が増えてもスマートなソースが書けそうです。

かわのくんとは

Web系IT企業でプログラミングやマネジメントをしています。趣味で音楽を少々。

Youtubeでライブ動画配信中

Ustreamでライブ動画配信中

スマートフォン向けにPCサイトを自動変換(コンバート)する『CONV2SP』 CSS作成支援ツール『CSSツクール』