はじめに
DynamoDBにも、AWS SDK for PHPは提供されているのですが、どうも使いにくいです。
というのも、検索や更新のパラメータや取得したデータのフォーマットが配列で、
すべて微妙に違うので覚えられない。つらい。ということで作りました。
使い方イメージ
ユーザーID 10番の人のデータを取得し、名前を John に更新する例
<?php
use Kettle\ORM;
$user = ORM::factory('User')->findOne(10);
$user->name = 'John';
$user->save();
ユーザーID 10番の人のつぶやきをすべて取得して出力する例
<?php
use Kettle\ORM;
$tweets = ORM::factory('Tweet')->where('user_id', 10)
->findMany();
foreach ($tweets as $tweet) {
echo $tweet->text . PHP_EOL;
}
こんな感じに、あまりDynamoDBを意識せずにObjectを操作する感覚で扱えます。
インストール
composer からインストールできます。
composer.json
{
"require": {
"kettle/dynamodb-orm": "dev-master"
}
}
- 上記のようなファイルを composer.json というファイル名で保存。
- 下記のコマンドを実行してインストールします。 composer.phar は別途インストールしてください。
$ composer.phar install
vendor 以下にインストールされます。
使うに際には、autoload.php をrequireしておけばクラス使用時に自動的にロードされるようになります。
<?php
require_once __DIR__.’/vendor/autoload.php’;
使い方
1. まずは設定
下記のような形で AWSの認証情報をセットします。
<?php
use Kettle\ORM;
ORM::configure("key", ‘YOUR_AWS_KEY');
ORM::configure("secret", ‘YOUR_AWS_SECRET');
ORM::configure("region", ‘YOUR_AWS_REGION');
// DynamoDB Localを使うには、base_url をセットします。
ORM::configure("base_url", ‘http://localhost:8000');
2. 次にテーブルに対応するEntityクラスを作成します。
実際に上記使い方イメージのように操作するには Userクラス、Tweetクラスが必要です。
ORMを継承したクラスに、テーブル名、ハッシュキー、レンジキー、カラムを定義します。
<?php
use Kettle\ORM;
class User extends ORM {
protected $_table_name = 'user';
protected $_hash_key = 'user_id';
protected $_schema = array(
'user_id' => 'N', // user_id is number
'name' => 'S', // name is string
'age' => 'N',
'country' => 'S',
);
}
class Tweets extends ORM {
protected $_table_name = ‘tweets’;
protected $_hash_key = 'user_id';
protected $_range_key = ‘timestamp’;
protected $_schema = array(
'user_id' => 'N',
‘timestamp’ => 'S',
‘text’ => ’S’,
‘is_deleted’ => ’N’,
);
}
DynamoDBはスキーマレスなので、定義とか必要なくなんでも突っ込めるのですが、
コードをメンテナンスする際に、データに何が入っているか把握できないと保守性が下がるため
上記のように定義しておく形を取っています。
Userクラスを使用する際には、次のようにfactoryメソッドを使いインスタンスを生成します。
<?php
$user = ORM::factory('User’);
3. データ操作(データの登録)
- create メソッドで空のインスタンスを作成
- 値を代入
- save()
<?php
$user = ORM::factory('User')->create();
$user->id = 1;
$user->name = 'John';
$user->age = 20;
$user->save();
4. データの操作(1レコードの取得)
- findOne にHashKeyの値を指定します。
<?php
$user = ORM::factory('User')->findOne(1);
echo $user->name. PHP_EOL;
// asArray() で連想配列になります。
print_r($user->asArray());
HashKeyとRangeKeyからなるテーブルの場合は両方を渡します。
$hoge = ORM::factory(‘Hoge’)->findOne(1, ‘fuga’);
5. データの操作(更新)
- findOneでデータを取得します。
- 新しいデータをセットします。
- save()
<?php
$user = ORM::factory('User')->findOne(1);
$user->age = 21;
$user->save();
6. データの操作(検索)
注意:
DynamoDBでは、インデックスを張ったカラムでしか検索ができません。
HashKey, RangeKey、Local Secondary Index、Global Secondary Index
などを事前に作成しておく必要があります。
- whereメソッドを使い条件を指定します。
- findMany で取得します。
<?php
$tweets = ORM::factory('Tweets')
->where('user_id', 1)
->where('timestamp', '>', 1397264554)
->findMany();
foreach ($tweets as $tweet) {
echo $tweet->text . PHP_EOL;
}
- whereメソッドは、引数が2つの時には、第1引数がkey名、第2引数が値になり、完全一致の検索となります。
- 引数が3つの場合には、第1引数がkey名、第2引数が比較演算子、第3引数が値となります。
- 比較演算子は、”=“, “<=“, “>“, “BETWEEN” などの一般的なものが使えます。
7. データの操作(GSIを使った検索)
Global Secondary Index を使って検索するには、下記のように
index() メソッドでインデックス名を指定します。
<?php
$users = ORM::factory('User')
->where('country', 'Japan')
->where('age', '>=', 20)
->index('country-age-index') // インデックス名を指定
->findMany();
8. データの操作(検索+クエリフィルタリング)
前述のとおり、DynamoDBはインデクスを指定した検索しかすることができませんが、
クエリフィルタリングという形で、検索した結果をフィルタして取得することができます。
下記の例は、is_deleted が 0 のものだけに絞って取得しています。
<?php
$tweets = ORM::factory('Tweets')
->where('user_id', 1)
->filter('is_deleted', 0) // using filter
->findMany();
進んだ使い方
1. シーケンスを管理するテーブルを作る
DynamoDBでは、MySQLのauto_incrementのように自動でカウントアップするカラムは作れませんが、工夫でauto_increment的な動きをするカラムを作ることができます。
sequence というテーブルを作っておきます。
HashKey は name (S) です。
<?php
use Kettle\ORM;
class Sequence extends ORM
{
protected $_table_name = "sequence";
protected $_hash_key = "name";
protected $_schema = array(
'name' => 'S',
'count' => 'N',
);
public function getNextId($name)
{
$this->name = $name;
$values = ['count' => 1];
$options = ['Action' => ['count' => 'ADD'], 'ReturnValues' => 'UPDATED_NEW'];
$result = $this->updateItem($values, $options);
$count = $result->getPath('Attributes/count/N');
return $count;
}
}
こんなクラスを作っておいて、
<?php
$user_id = ORM::factory('Sequence')->getNextId('user_id');
var_dump($user_id);
こんな感じで新しいIDを取り出します。何度も呼び出すとカウントアップされているのが分かります。
user_id の部分は、任意の文字列でOKです。
おわりに
バグ等ありましたら Githubの方に報告をお願いします。
https://github.com/inouet/kettle