php-activerecord というORMライブラリを一部だけ読みました.
http://www.phpactiverecord.org/
github : jpfuentes2/php-activerecord
名前のとおり ActiveRecord パターンのphp実装で、Ruby on Rails に影響されているようです.
インストール
もちろん composer でインストールできます.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ curl -sS https://getcomposer.org/installer | php
$ vi composer.json
{
"require" : {
"php-activerecord/php-activerecord" : "*"
}
}
$ php composer.phar install
Loading composer repositories with package information
Installing dependencies ( including require-dev)
- Installing php-activerecord/php-activerecord ( v1.1.2)
Downloading: 100%
Downloading: 100%
使い方
README に詳しく書いてあります.
読む
Convention over Configuration により少ないコード量でCRUD操作ができる
テーブルまたはビューのレコードとインスタンスが 1 対 1 で関連づく
このあたりの特徴の実装を、下記 Read 操作をした場合で見ていきます.
1
2
<?php
Post :: find_by_name ( 'foo' );
未定義の static メソッドなので Model::__callStataic が呼ばれ、第1引数で受け取るメソッド名($method)を使って取得条件を決定します.
Model.php
1
2
3
4
5
6
7
8
9
10
<?php
public static function __callStatic ( $method , $args )
{
// ...
if ( substr ( $method , 0 , 7 ) === 'find_by' )
{
$attributes = substr ( $method , 8 );
$options [ 'conditions' ] = SQLBuilder :: create_conditions_from_underscored_string ( static :: connection (), $attributes , $args , static :: $alias_attribute );
取得条件を決定する SQLBuilder:: create_conditions_from_underscored_string は、find_by 以降の部分を正規表現で分解・置換して、SQLを組み立てています.
SQLBuilder.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
public static function create_conditions_from_underscored_string ( Connection $connection , $name , & $values = array (), & $map = null )
{
// ...
$parts = preg_split ( '/(_and_|_or_)/i' , $name , - 1 , PREG_SPLIT_DELIM_CAPTURE );
// ...
$conditions [ 0 ] .= preg_replace ( array ( '/_and_/i' , '/_or_/i' ), array ( ' AND ' , ' OR ' ), $parts [ $i - 1 ]);
// ...
$bind = is_array ( $values [ $j ]) ? ' IN(?)' : '=?' ;
これで、Read操作の動的メソッドの仕組みがわかりました.続いて、先ほどの Model::__callStatic に戻って、テーブルとの関連付け(参照先テーブルの決定)を見ていきます.
Model::find に取得条件を渡して、結果を返しています. $create は、 “find_or_create_by_***” で始まるメソッド(該当レコードがなければ作成)を呼び出した場合に true が入ります.
Model.php
1
2
3
4
5
<?php
if ( ! ( $ret = static :: find ( 'first' , $options )) && $create )
return static :: create ( ....
return $ret ;
Model::find では、取得条件の妥当性を確認した後、Table::find に処理を委譲しています.
Model.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
public static function find ( /* $type, $options */ )
{
//...
$args = func_get_args ();
$options = static :: extract_and_validate_options ( $args );
// ...
$list = static :: table () -> find ( $options );
return $single ? ( ! empty ( $list ) ? $list [ 0 ] : null ) : $list ;
}
public static function table ()
{
return Table :: load ( get_called_class ());
}
Table::load() でインスタンスを生成するタイミングで、対象のテーブル名が決定します.
Table.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
public static function load ( $model_class_name )
{
self :: $cache [ $model_class_name ] = new Table ( $model_class_name );
// ...
}
public function __construct ( $class_name )
{
// ...
$this -> set_table_name ();
}
テーブル名はデフォルトでクラス名が使われ(Utils::pluralizeによって複数形等の変換がされる)、 $table または $table_name スタティック変数が定義されていればそれが優先されます.
Table.php
1
2
3
4
5
6
7
8
9
<?php
private function set_table_name ()
{
if (( $table = $this -> class -> getStaticPropertyValue ( 'table' , null )) || ( $table = $this -> class -> getStaticPropertyValue ( 'table_name' , null )))
$this -> table = $table ;
else
{
$this -> table = Inflector :: instance () -> tableize ( $this -> class -> getName ());
// ...
中途半端ですが集中力が切れたのでこれで終わりにします…