[PHP] Atlas.Pdoの使い方
こんにちは、ナカエです。
今回から、前回の記事で紹介したAtlasのパッケージの使い方をひとつずつ見ていこうと思います。
今日はAtlas.Pdoの紹介です。
PDOのラッパー
Atlas.PdoはPDOの薄いラッパーであり、素のPDOをより使いやすくしたものです。
- 記述の簡略化
- Generatorを利用したyield*()のメソッド
- クエリロガーの設定
などの特徴があります。
インストール
Packagistにてatlas/pdoとして公開されています。 要求されるPHPのバージョンは>=7.1です。
Composerを利用していれば簡単に導入できます。
composer require atlas/pdo
composer.jsonの例
{
"require": {
"php": "^7.1",
"ext-pdo": "*",
"atlas/pdo": "^1.1"
}
}
コネクションオブジェクトのインスタンス化
Atlas.Pdoの中心となるのは\Atlas\Pdo\Connectionクラスです。 まずはこのオブジェクトをインスタンス化することから始まります。
dsnを指定して作成
コンストラクタを直接使うのではなく、スタティックメソッドの\Atlas\Pdo\Connection::new()を使うことを推奨しているようです。 基本的にはPDOのコンストラクタと同じシグネチャになっており、第一引数にdsn、第二引数にユーザ名、第三引数にパスワード、第四引数にPDOのオプションを指定できます。
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use \Atlas\Pdo\Connection;
$connection = Connection::new(
'mysql:host=localhost;dbname=testdb',
'username',
'password'
);
PDOのインスタンスから作成
\Atlas\Pdo\Connection::new()の第一引数にPDOのインスタンスを指定した場合はそのインスタンスがそのまま使われます。
/** @var \Pdo $pdo */
$connection = Connection::new($pdo);
ファクトリで作成
\Atlas\Pdo\Connection::factory()にnew()と同じ引数を与えて、遅延作成することも可能です。
// newと同様の引数で作成
$factory = Connection::factory('sqlite::memory:');
// あとで初期化できる。$factoryは\Atlas\Pdo\Connectionを返すcallableになっている
$connection = $factory();
PDOのメソッドを使う
\Atlas\Pdo\Connectionを使うと、PDOのメソッドを使う処理を少ない記述量で利用することができます。
exec(), prepare(), query()
PDOのメソッドをそのままラップしたものです。 例えばexec()は指定したステートメントを実行します。
$factory = Connection::factory('sqlite::memory:');
/** @var Connection $connection */
$connection = $factory();
$connection->exec('CREATE TABLE users (id integer primary key, name varchar(255))');
perform()
bindする値を指定してプリペアドステートメントを実行します。
$records = [
[
'id' => 1,
'name' => 'Taro',
],
[
'id' => 2,
'name' => 'Hanako',
],
[
'id' => 3,
'name' => 'Jiro',
],
];
foreach($records as $record) {
$connection->perform('INSERT INTO users (id, name) VALUES (:id, :name)', $record);
}
fetch*()
fetch*()系のメソッドは、PDOのprepare()やbindValue()の記述を短縮し、ボイラープレートコードを減らすためのものです。 fetchAl()のほか、fetchColumn(), fetchObjects(), fetchOne()など、fetchの戻り値のスタイルごとにメソッドが用意されています。
$stm ='SELECT * FROM users WHERE name LIKE :name';
$bind = ['name' => '%ro'];
$results = $connection->fetchAll($stm, $bind);
var_dump($results);
/**
array(1) {
[0]=>
array(2) {
["id"]=>
string(1) "1"
["name"]=>
string(4) "Taro"
}
}
*/
yield*()
yeild()のメソッドはfetch()と対応するメソッドが揃っています。 結果を全てまとめて取得するfetch*()に対し、Generatorを使ってメモリ使用量を節約できるのが異なる点です。
yield
$stm2 ='SELECT * FROM users';
$results = $connection->yieldAll($stm2);
assert($results instanceof \Generator);
foreach($results as $result) {
var_dump($result);
}
トランザクション
beginTransaction(), commit(), rollBack()は通常のPDOと同様に使えます。
$connection->beginTransaction();
try {
$connection->perform('INSERT INTO users (id, name) VALUES (:id, :name)', ['id' => 4, 'name' => 'Tom']);
$connection->perform('INSERT INTO users (id, name) VALUES (:id, :name)', ['id' => 3, 'name' => 'Mary']);
$connection->commit();
} catch (\Exception $e) {
echo 'クエリの実行に失敗しました' . $e->getMessage() . PHP_EOL;
$connection->rollBack();
}
クエリロガー
コネクションオブジェクトのログ機構を有効化すると、独自のロガーをセットして実行されたクエリを記録したり、実行後にクエリの情報を取り出したりすることができます。
class MyQueryLogger {
public function __invoke(array $entry)
{
echo PHP_EOL . 'クエリ実行:' . PHP_EOL;
var_dump($entry);
}
}
$connection->logQueries(true);
$connection->setQueryLogger(new MyQueryLogger());
$connection->beginTransaction();
$stm3 ='SELECT * FROM users WHERE id = 3';
$connection->fetchAll($stm3);
$connection->commit();
クエリの情報は配列で、
- 'start': 実行開始日時
- 'finish': 実行終了日時
- 'duration': 実行にかかった時間
- 'statement': 実行したステートメント
- 'values': バインドされた値
- 'trace': クエリが実行された箇所のスタックトレース
の情報を含みます。トランザクションの各メソッドの処理もログに含まれます。 ロガーを使わない場合は、Connection::getQueries()で後から取得することもできます。
クエリ実行:
array(6) {
["start"]=>
float(1542610191.0353)
["finish"]=>
float(1542610191.0353)
["duration"]=>
float(8.1062316894531E-6)
["statement"]=>
string(38) "Atlas\Pdo\Connection::beginTransaction"
["values"]=>
array(0) {
}
["trace"]=>
string(229) "#0 /Users/Nextat/Workspace/atlaspdo_test/vendor/atlas/pdo/src/Connection.php(84): Atlas\Pdo\Connection->addLogEntry(Array)
#1 /Users/Nextat/Workspace/atlaspdo_test/index.php(98): Atlas\Pdo\Connection->beginTransaction()
#2 {main}"
}
クエリ実行:
array(6) {
["start"]=>
float(1542610191.0355)
["finish"]=>
float(1542610191.0355)
["duration"]=>
float(1.7166137695312E-5)
["statement"]=>
string(32) "SELECT * FROM users WHERE id = 3"
["values"]=>
array(0) {
}
["trace"]=>
string(641) "#0 /Users/Nextat/Workspace/atlaspdo_test/vendor/atlas/pdo/src/Connection.php(123): Atlas\Pdo\Connection->addLogEntry(Array)
#1 /Users/Nextat/Workspace/atlaspdo_test/vendor/atlas/pdo/src/LoggedStatement.php(41): Atlas\Pdo\Connection->Atlas\Pdo\{closure}(Array)
#2 /Users/Nextat/Workspace/atlaspdo_test/vendor/atlas/pdo/src/Connection.php(138): Atlas\Pdo\LoggedStatement->execute()
#3 /Users/Nextat/Workspace/atlaspdo_test/vendor/atlas/pdo/src/Connection.php(186): Atlas\Pdo\Connection->perform('SELECT * FROM u...', Array)
#4 /Users/Nextat/Workspace/atlaspdo_test/index.php(100): Atlas\Pdo\Connection->fetchAll('SELECT * FROM u...')
#5 {main}"
}
クエリ実行:
array(6) {
["start"]=>
float(1542610191.0356)
["finish"]=>
float(1542610191.0356)
["duration"]=>
float(5.0067901611328E-6)
["statement"]=>
string(28) "Atlas\Pdo\Connection::commit"
["values"]=>
array(0) {
}
["trace"]=>
string(220) "#0 /Users/Nextat/Workspace/atlaspdo_test/vendor/atlas/pdo/src/Connection.php(92): Atlas\Pdo\Connection->addLogEntry(Array)
#1 /Users/Nextat/Workspace/atlaspdo_test/index.php(101): Atlas\Pdo\Connection->commit()
#2 {main}"
}
まとめ
あまり複雑なことはしていないため、PDOを知っていれば、PDOをラップして記述量を減らしてロギング機構を入れたものとしてすんなり使えます。 個人的にはyield*()系のメソッドが簡単に使えるのが気に入っています。
Atlas.PdoにはConnectionに加えてConnection Locatorの機能があります。次回はその機能について紹介します。 → [PHP] Atlas.Pdoの使い方その2 ConnectionLocator
追記
サンプルコードを書いている最中にクエリロガーのバグを踏んだけどすでにPRが出されていた Add default datatype for LoggedStatement->bindValue according to parent class