doctrine/dbal をバックエンドにしたシンプルなテーブルゲートウェイ。
composer require ngyuki/doctrine-table-gateway:dev-master
use ngyuki\DoctrineTableGateway\TableGateway;
// Connection のインスタンスとテーブル名をコンストラクタに指定してインスタンスを生成する
$t = new TableGateway($connection, 'example');
// find($id) で主キーで 1 件の行を array で返す
$t->find(1);
// find() は行が見つからない場合は null を返す
$t->find(9999);
// all() ですべての行のイテレーターが返る
$t->all();
scope()
で find()
や all()
の結果の範囲を絞ることができます。
// `scope()` でテーブルにスコープを適用する
$t->scope('a = 1')->all();
// => WHERE (a = 1)
// スコープは連想配列でも指定できる
$t->scope(['a' => 1])->all();
// => WHERE (a = 1)
// スコープの整数キーの値は SQL の式としてそのまま使用される
$t->scope(['a = 1', 'b = 2', 'c' => 3])->all();
// => WHERE (a = 1) AND (b = 2) AND (c = 3)
// スコープはチェインできる
$t->scope(['a' => 1])->scope(['b' => 0])->all();
// => WHERE (a = 1) AND (b = 2)
// チェインや配列で複数指定されたスコープは AND で連結される
$t->scope('a = 1 OR b = 2')->scope(['c = 3', 'd' => 4])->all();
// => WHERE (a = 1 OR b = 2) AND (c = 3) AND (d = 4)
// スコープを適用したオブジェクトは使いまわすことができる
$a = $t->scope('a = 1');
$b = $a->scope('b = 2');
$c = $a->scope('c = 3');
$a->all(); // => WHERE (a = 1)
$b->all(); // => WHERE (a = 1) AND (b = 2)
$c->all(); // => WHERE (a = 1) AND (c = 3)
// orderBy() で並び順を指定する
$t->scope('a = 1')->orderBy('b', 'DESC')->all();
// => WHERE (a = 1) ORDER BY b DESC
スコープにはクロージャーが指定できます。クロージャーの引数の Doctrine\DBAL\Query\QueryBuilder
を用いて自由にクエリをカスタマイズできます。
$t->scope(function (\Doctrine\DBAL\Query\QueryBuilder $q) {
return $q->where('a = 1')->andWhere('b = 2')->orderBy('c', 'desc');
}); // => WHERE (a = 1) AND (b = 1) ORDER BY c DESC
all()
メソッドが返すイテレータにはいくつかの便利メソッドが定義されています。
// 指定された列値のみのイテレーターを返す
$t->all()->asColumn('col');
// 配列なら指定した列値の配列が返る
$t->all()->asColumn(['col1', 'col2']);
// 指定された列値がキー値となるイテレーターを返す
$t->all()->asUnique('col');
// 指定された 2 つの列値がキーと値となるイテレーターを返す
$t->all()->asPair('key', 'val');
// 値の方は asColumn と同じように指定できる
$t->all()->asPair('key', ['col1', 'col2']);
// 複数の列値で多次元連想配列を返す ... `$array[$key1][$key2][$key2] = $val `のような連想配列が返る
$t->all()->asGroup(['key1', 'key2', 'key2'], 'val');
// 値の方は asColumn と同じように指定できる
$t->all()->asGroup(['key1', 'key2', 'key2'], ['col1', 'col2']);
// 値を NULL にすると行がそのまま返る
$t->all()->asGroup(['key1', 'key2', 'key2'], null);
// イテレーターを配列化する
$t->all()->toArray();
例えば次のようなテーブルがあるとき、
id | name | age |
---|---|---|
1 | aaa | 16 |
2 | bbb | 24 |
3 | ccc | 32 |
それぞれ次のように返ります。
$t->all()->asColumn('name')->toArray();
// [aaa, bbb, ccc]
$t->all()->asColumn(['id', 'age'])->toArray();
// [[1, 16], [2, 24], [3, 32]]
$t->all()->asUnique('id')->toArray();
// [
// 1 => [id => 1, name => aaa, age => 16],
// 2 => [id => 2, name => bbb, age => 24],
// 3 => [id => 3, name => ccc, age => 32],
// ]
$t->all()->asPair('id', 'name');
// [
// 1 => aaa,
// 2 => bbb,
// 3 => ccc,
// ]
$t->all()->asGroup(['id', 'age'], 'name');
// [
// 1 => [
// 16 => aaa
// ],
// 2 => [
// 24 => bbb,
// ],
// 3 => [
// 32 => ccc,
// ],
// ]
$t->insert(['a' => 1, 'b' => 2, 'c' => 3]);
#=> INSERT INTO ... (a, b, c) VALUES (1, 2, 3)
INSERT で存在しない列名を指定しても無視されます。
$t->insert(['a' => 1, 'xxx' => 2]);
#=> INSERT INTO ... (a) VALUES (1)
スコープの第2引数で INSERT のデフォルト値を指定できます。
$t->scope('a = 1', ['a' => 1])->insert(['b' => 2, 'c' => 3]);
#=> INSERT INTO ... (a, b, c) VALUES (1, 2, 3)
スコープの第2引数が省略された場合は、第1引数の key => value
の形式の値のみがデフォルト値として使用されます。
$t->scope('a = 1', ['b' => 2])->insert(['c' => 3]);
#=> INSERT INTO ... (b, c) VALUES (2, 3)
update($data)
で指定したデータで、スコープのすべての行を UPDATE します。
$t->scope('a = 1')->update(['b' => 2, 'c' => 3]);
#=> UPDATE ... SET b = 2, c = 3 WHERE a = 1
update($data)
で存在しない列を指定しても無視されます。
$t->scope('a = 1')->update(['b' => 2, 'xxx' => 9]);
#=> UPDATE ... SET b = 2 WHERE a = 1
update($data)
でスコープのすべての行を DELETE します。
$t->scope('a = 1')->delete();
#=> DELETE FROM ... WHERE a = 1
主キーを指定したいときは by($id)
でスコープを適用してください。
$t->by(1)->update(['a' => 1, 'b' => 2, 'c' => 3]);
#=> UPDATE ... SET a = 1, b = 2, c = 3 WHERE id = 1
$t->by(1)->delete();
#=> DELETE FROM ... WHERE id = 1
スコープが適用されていないときはすべての行が対象になります。
$t->update(['a' => 1, 'b' => 2, 'c' => 3]);
#=> UPDATE ... SET a = 1, b = 2, c = 3
$t->delete();
#=> DELETE FROM ...
デフォルトでは TableGateway がインスタンス化されるたびにテーブル定義のメタデータをデータベースから取得しますが、次のようにキャッシュを使うことができます。
use ngyuki\DoctrineTableGateway\TableGateway;
use ngyuki\DoctrineTableGateway\Metadata;
$t = new TableGateway($connection, 't_user', new Metadata($connection, $cache));
$cache
には PSR-16 の Psr\SimpleCache\CacheInterface
をインプリメントしたオブジェクトが指定できます。
cache/doctrine-adapter
doctorine のキャッシュを使う場合はは cache/doctrine-adapter が使えます。
composer require cache/doctrine-adapter
use ngyuki\DoctrineTableGateway\TableGateway;
use ngyuki\DoctrineTableGateway\Metadata;
use Doctrine\Common\Cache\ApcuCache;
use Cache\Adapter\Doctrine\DoctrineCachePool;
$cache = new DoctrineCachePool(new ApcuCache());
$t = new TableGateway($connection, 't_user', new Metadata($connection, $cache));
そういうのはできない。
OneToMany/ManyToOne/ManyToMany のような関係でどのようにテーブルを走査すればいいかは機械的に判断できるものではないと思うので。
どうしてもやりたければ scope()
にクロージャーを渡してクエリビルダをごちゃごちゃすればできると思います。
README.md にかかれていない機能
- query
- 指定した SQL をそのまま実行して結果セットを得る
- transactional
- コールバック関数をトランザクションの中で実行する
- ExpressionBuilder
- scope の表現が少し豊かになった