PHP勉強会@東京

日曜日、秋葉原にてPHP勉強会がありました。いやー、秋葉原めちゃめちゃかわってますね・・・ちょっといってない間にこんなことになってますかーと。

で、昨日の勉強会向けの隠し玉ですが、「ActiveRecord」でした。(LLDNの時の昼飯をファーストキッチンで食べていたんですが、ActiveRecordが隠し玉なんですよーって話をbobchinさんにしていたら、われわれの後ろにいたかずひこさん達Rubyチームに捕捉されて、「ActiveRecordっていう単語がでると反応しちゃいますよねー」というような会話が始まっていた・・・)

Interceptorを使って実現するので、雰囲気としてはS2DaoなアプローチでActiveRecordを仕立てたという感じです。(昨日の勉強会で由来を説明してなかったので、私オリジナルの考えと思った方がいたらすみません。オリジナルはS2Daoです)

で、どんなものかというと、以下のようなクラスをつくって、InterceptorでひっかけてSQLの自動生成しちゃおうってことです。

class Member {
    function findAll() {
    }
    function find($id) {
    }
    function save() {
    }
    function delete() {
    }
}

S2DaoなアプローチのActiveRecordといってるのは次のような動きをするからです。

  1. クラス名がテーブル名に直結する(将来的にはコメントアノテーションで微妙に異なるテーブル名との紐付けを可能にしようと思っています)
  2. テーブルはオートインクリメントなid属性(これもコメントアノテーションで名前を変更可能にする予定)をもつ
  3. findAll/find/save/deleteというメソッドを定義だけしておいて、中身は基本的に書く必要なし(これは最終的にはちょっとしたトリックを思いついたのだが、それは後述)。各メソッドに「ActiveRecordInterceptor」をかけておけばそのメソッドが呼ばれたときにInterceptor側でSQLを自動生成する。その際に、テーブルのフィールド情報を動的に取得して(現在の実装ではPEAR::DBのtableInfoを使ってます)、それを使用してSQLの生成をします。
  4. findしてからsaveするとupdate文が生成されて、値を入れてsaveだけするとinsertになって、findしてからdeleteするとdelete文が実行されるという動きをします。
  5. findAllはそのテーブルのデータを select * from 〜 でとってきて、元クラス(この例だとMember)の配列として返す。
  6. one-to-oneやone-to-manyな関係くらいまではフォローする予定(昨日のものではまだ実装してない)。そのあたりの指定はコメントアノテーションを使うつもり。
  7. 複雑なSQLが必要な場合はquery()メソッドをつかってSQL文を直接なげてくれ!という感じにする(これも昨日のものでは実装してない)

で、いろいろ考えていた時に思いついたのが(3)に関して、ActiveRecordInterceptorとしては中身は必要としないが、Interceptorをかける元クラスをモッククラスとして使ってはどうかということを考えた。つまり、最初にダミーの値を返すクラスをつくっておいて、それで開発をすすめて、そのモッククラスに対してInterceptorをかけるとそのままSQL自動生成になるってのは結構いいかもと。これなら現場的にもInterceptorをつかって実現するメリットがでてくるかなと。

というものを作ってデモをしてみたのですが、みんなの反応として関心していただいたととったのですが、基本的にはドン引きって感じの雰囲気に・・・ま、ある程度衝撃を走らせることができたと自分的には納得させて、ActiveRecordInterceptorの開発を続けて行こうと思います。

今週末 9/3 のPHP関西セミナーでもDIxAOPの説明としてActiveRecordInterceptorのデモを行いたいと思います。関西の方、少々お待ちを。