Displaying posts written in

2月 2010

CakePHPでSchemalessなデータを扱う

拙作のKeyValueSource(key-valueストアのためのDataSource)で、スキーマ・レスなデータを保存できるようにしてみました。

cakephp-key-value-source – GitHub
(このプロジェクトは実用より実験を重視しているので、頻繁に仕様が変わります)

モデルクラスで $looseSchema というプロパティを設定すると、プライマリキーidを除いて、どんな構造のデータでも保存できるようになります。

class SchemalessUser extends AppModel {
  var $useDbConfig = 'memcache';
  var $looseSchema = true;
}
 
// in Controllers 
$SchemalessUser->save(array('id' => 1234,
                            'name' => 'John Smith',
                            'hobby' => array('baseball', 'soccer')));
$SchemalessUser->save(array('id' => 1235,
                            'firstname' => 'John',
                            'lasttname' => 'Smith',
                            'age' => 20));

saveメソッドの fieldList オプションでキーを制限することができます。

$SchemalessUser->save(array('id' => 1234,
                            'name' => 'John Smith',
                            'hobby' => array('baseball', 'soccer')),
                      array('fieldList' => array('id', 'name')));
 
// hobby は保存されない
// array('id' => 1234, 'name' => 'John Smith')
$ret = $SchemalessUser->read(null, 1234);

$looseSchema プロパティで一部のスキーマを明示的に設定しておくこともできます。下の例ではプライマリキーを key に変更し、更新日時を入力するために updated フィールドを定義しています。

class LooseSchemaUser extends AppModel {
  var $useDbConfig = 'memcache';
  var $primaryKey = 'key';
  var $looseSchema = array('key' => array('type' => 'string', 'length' => 255),
                           'updated' => array('type' => 'datetime'));
}
 
// in Controllers 
$LooseSchemaUser->save(array('key' => 1234,
                             'name' => 'John Smith'));
 
// array('key' => 1234, 'name' => 'John Smith', 'updated' => '2010-02-07 22:23:13')
$ret = $LooseSchemaUser->read(null, 1234);

$looseSchemaを設定せず、$_schema を設定すれば通常の動作になります。

class StrictUser extends AppModel {
  var $useDbConfig = 'memcache';
  var $_schema = array('id' => array('type' => 'string', 'length' => 255),
                       'name' => array('type' => 'string'));
}
 
// in Controllers 
$StrictUser->save(array('id' => 1234,
                        'name' => 'John Smith',
                        'hobby' => array('baseball', 'soccer')));
 
// hobby は保存されない
// array('id' => 1234, 'name' => 'John Smith')
$ret = $StrictUser->read(null, 1234);

裏側では KeyValueLooseSchemaBehavior というBehaviorを使って、$_schema に存在しないデータを一旦別のフィールドに退避しておき、DataSource の中で取り出しています。退避用のフィールドはデフォルトでは _schemaless_data という名前で、DataSource の describe メソッドの中で組み込まれます。さらに(ちょっと反則気味ですが) Behavior 自体も describe メソッドの中で $actsAs に設定することで、特別な設定なしで動くようになっています。

自動設定される部分を書き下すと、おおむね次のようになります。

class User extends AppModel {
  var $actsAs = array('KeyValueLooseSchema' => array('schemalessField' => '_schemaless_data'));
  var $_schema = array('id' => array('type' => 'string', 'length' => 255),
                        '_schemaless_data' => array('type' => 'schemaless'));
}

KeyValueLooseSchemaBehavior の実装は汎用的になっているので、他の DataSource にほとんどそのまま持っていけるはずです。