ngyuki/doctrine-table-gateway

Simple TableGateway for doctrine-dbal

v0.0.1 2018-01-15 10:19 UTC

This package is auto-updated.

Last update: 2022-11-22 11:03:19 UTC


README

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,
//      ],
//  ]

INSERT

$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/DELETE

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 の表現が少し豊かになった