脳みそスワップアウト

揮発性なもので。おもにPHPのこととか。

illuminate/database

私の携わったプロジェクトではORMが採用されないケースが多い。
メリットデメリットともにあるし、向き不向きもあるし、アレルギーを持ってる人がいたりするし、論争に興味もないのでそれ自体はいいのだけど、
たとえORMを使わない場合でも、クエリビルダだけは欲しいと思うことが多々ある。

「col1 = 'A' AND col2 = 'B'」のような同じ抽出条件が何度も出てきて、
それをベースにさらにwhere句を追加したSQLを書くような場合、
頻繁に出てくるwhere句を共通化したいのだけど、
文字列でSQLを組み立てているとだんだん訳が分からなくなってくる。

要はSQLの組み立てを構造的に行いたいのだ。
ORMはクエリビルダの機能を含んでいることが多いので、そこだけ使うことができないか調べてみた。

PHPのORMライブラリの候補はいくつかある

  • Doctrine2
    PHPでいちばん有名なORM。Symfony2で使われている。
    DQLというSQLライクではあるが独自の文法を覚えないとならない。
    ちょっとヘビーすぎるし、ORM嫌いな人には採用されないだろうということでパス。

  • FluentPDO
    今回はパス。

  • Idiorm
    今回はパス。

  • CakePHP3 cakephp/orm
    クエリビルダだけ利用できたら使いやすそうなのだけど、
    ORM/クエリビルダ部分として単品で使うことができず、
    フレームワークのコアまで要求されるのでパス。

  • illuminate/database
    Laravelで使われている。
    この中では一番hotそうなのでこれを試してみた。

今回は illuminate/database 5.2.7 を触ってみた。

Laravelではクエリビルダだけでなく Eloquent というORM層まで使うことが前提の内容なのだけど、基本的には Laravelのクエリビルダのドキュメント にクエリビルダのAPIも載っている。
括弧は関数で書くというのが特徴で、それ以外は直感的に使える感じ。

クエリビルダのみを使うにはこんな感じになる。(MySQLの場合)

$pdo = new \PDO($dsn);

$iConnection = new \Illuminate\Database\Connection($pdo);
$grammar = new \Illuminate\Database\Query\Grammars\MySqlGrammar();
$processor = new \Illuminate\Database\Query\Processors\MySqlProcessor();

$qb = new \Illuminate\Database\Query\Builder($iConnection, $grammar, $processor);

$q = $qb->newQuery();
$q->from('users')->where('user_id', '>=', '100');
$q->orWhere('age', '>=', 20);
var_dump($q->toSql(), $q->getBindings());

toSql()プレースホルダ(?)付きのSQL文、getBindings() で値が配列で取得できるので、これを PDO::prepare()PDOStatement::execute() で実行すればいい。
(実行自体もクエリビルダでできるけど、ORM不採用プロジェクトなら固有のPDOラッパがあるはずなので)

ざっと使った感想

  • SQLステートメントがすべて小文字になる
  • where, select など、部分的なSQLを得ることができない。
    残念ながらwhere部分のみを得ることができないので、SQL全体をこれで構築することになる。
  • selectカラムの追加は addSelect() でできるが、同一カラム名を追加すると重複してしまう。
  • 集計系(count, max, min, avg, sum) は、即時にSQLが実行されてしまう。 今回の用途の場合は selectRaw() で書く必要がある。
$q = $qb->newQuery()->from('users')->selectRaw('COUNT(id) cnt');
  • サブクエリは whereRaw() ですることになる。
    つまり文字列の組み立てになってしまうということ・・。
$q = $qb->newQuery()->from('users')
  ->where('gender', '=', 'f')
  ->whereRaw('users.id IN (SELECT user_id FROM dept WHERE col1 = ?)', [1]);
  • SELECT や WHERE に部分的に生クエリを書けるので、クエリビルダとしては自由度が高く、結構いろいろ書ける。
  • 疑問符プレースホルダとなる

これくらいなら、ORM嫌いなチームでの採用もできるかもしれない。
でもサブクエリが文字列組み立てなら意味ないか。

FluentPDO と Idiorm も時間ができたら試してみよう。