S2Base.PHP5をなでる(Entity、Dao作成編)

前回までで、プロジェクトとモジュールの構築が完了しました。
今度はEntityとDaoの順番でコンポーネントを作成していきます。
で、せっかくなのでSQLiteを使って、実際にRDBMSにアクセスしてみます。
今回はamfphpプラグインのQuickStartで使ったDBを使います。
テーブル構造はQuickStartのOverviewにあります。
それからDBファイルは、QuickStartのDataBaseからダウンロードできます。

DataBaseの準備

先程のURLからダウンロードしたSQLiteのDBファイルをsampleプロジェクトのvar/dbディレクトリにコピーします。
更新処理も試しますので、コピーしたファイルに書き込み権限をつけてください。

次にDBの接続設定を行います。
S2Base.PHP5では、DBへの接続設定をS2Daoのpdo.diconで行います。
pdo.diconはsampleプロジェクトのapp/commons/daoディレクトリにあります。
pdo.diconは、こんな風に記載されていると思います。

 
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components.dtd">
<components namespace="pdo">
 
    <!--
    <component name="dataSource" class="S2Container_PDODataSource">

        <property name="dsn">"mysql:host=localhost; dbname=s2con"</property>
        <property name="user">"root"</property>
        <property name="password">"pass"</property>
        <property name="option">
            array(PDO::ATTR_ORACLE_NULLS => PDO::NULL_EMPTY_STRING,
                  PDO::ATTR_AUTOCOMMIT => true);
        </property>
    </component>
    -->
 
    <component name="dataSource" class="S2Container_PDODataSource">
        <property name="dsn">return 'sqlite:' . S2BASE_PHP5_VAR_DIR . '/db/s2base.db'</property>
    </component>
 
    <!--
    <component name="dataSource" class="S2Container_PDODataSource">

        <property name="dsn">"pgsql:host=localhost; dbname=s2con"</property>
        <property name="user">"root"</property>
        <property name="password">"pass"</property>
        <property name="option">
            array(PDO::ATTR_ORACLE_NULLS => PDO::NULL_EMPTY_STRING,
                  PDO::ATTR_AUTOCOMMIT => false);
        </property>
    </component>
    -->
 
    <!--
    <component name="dataSource" class="S2Container_PDODataSource">

        <property name="dsn">"firebird:host=localhost; dbname=/path/to/s2con.fdb"</property>
        <property name="user">"root"</property>
        <property name="password">"pass"</property>
        <property name="option">
            array(PDO::ATTR_ORACLE_NULLS => PDO::NULL_EMPTY_STRING,
                  PDO::ATTR_AUTOCOMMIT => false);
        </property>
    </component>
    -->
 
    <!--
    <component name="dataSource" class="S2Container_PDODataSource">

        <property name="dsn">"oci:dbname=//localhost/s2con"</property>
        <property name="user">"root"</property>
        <property name="password">"pass"</property>
        <property name="option">
            array(PDO::ATTR_ORACLE_NULLS => PDO::NULL_EMPTY_STRING,
                  PDO::ATTR_AUTOCOMMIT => false);
        </property>
    </component>
    -->
 
    <!-- freetds dsn sybase|mssql -->
    <!--
    <component name="dataSource" class="S2Container_PDODataSource">

        <property name="dsn">"dblib:host=localhost; dbname=s2con"</property>
        <property name="user">"root"</property>
        <property name="password">"passpass"</property>
        <property name="option">
            array(PDO::ATTR_ORACLE_NULLS => PDO::NULL_EMPTY_STRING);
        </property>
    </component>
    -->
 
    <component name="requiredTx" class="S2Dao_RequiredInterceptor" />
    <component name="requiresNewTx" class="S2Dao_RequiresNewInterceptor" />
    <component name="mandatoryTx" class="S2Dao_MandatoryInterceptor" />
    <component name="neverTx" class="S2Dao_NeverInterceptor" />
    <component name="notSupportedTx" class="S2Dao_NotSupportedInterceptor" />
 
</components>
 
今回は先程ダウンロードしたsample.dbを使いますので、pdo.diconを以下の様に修正します。
 
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container//EN"
"http://www.seasar.org/dtd/components.dtd">
<components namespace="pdo">
 
    <component name="dataSource" class="S2Container_PDODataSource">
        <property name="dsn">return 'sqlite:' . S2BASE_PHP5_VAR_DIR . '/db/sample.db'</property>
    </component>
 
    <component name="requiredTx" class="S2Dao_RequiredInterceptor" />
    <component name="requiresNewTx" class="S2Dao_RequiresNewInterceptor" />
    <component name="mandatoryTx" class="S2Dao_MandatoryInterceptor" />
    <component name="neverTx" class="S2Dao_NeverInterceptor" />
    <component name="notSupportedTx" class="S2Dao_NotSupportedInterceptor" />
 
</components>
 
以上で、DBの接続設定は完了です。

Entity作成

続いてデータ型となるEntityを作成します。
sample.dbには、s2base_pluginというテーブルがあって、idとnameというカラムがあります。
このテーブルのデータを表現するS2BasePluginというEntityを作ります。
まずはsampleプロジェクトディレクトリでs2baseコマンドを実行して、コンソールを起動します。

% s2base
コマンドのリストが表示されたら、Entityコマンドを表す3を入力してエンターキーを押したら、
以下の様に質問に答えていきます。
 
[ Module list ]
0 : (exit)
1 : sample_module
choice ? : 1  <-sample_moduleを表す1を入力してエンターキー
 
use database ? (y/n) : y  <-yを入力してエンターキー
 
[ table list ]
0 : (exit)
1 : s2base_plugin
2 : sqlite_sequence
choices ? (1,2,--,) : 1  <-s2base_pluginテーブルを表す1を入力してエンターキー
 
entity class name ? [S2base_pluginEntity] : S2BasePlugin  <-Entity名となるS2BasePluginを入力してエンターキー
 
[ generate information ]
  module name          : sample_module
  entity class name    : S2BasePlugin
  table name           : s2base_plugin
  columns              : id, name
 
confirm ? (y/n) : y  <-yを入力してエンターキー
 
use database ?の質問にyと答えると、DB接続設定を使ってDBに接続してテーブルの一覧をリストしてくれます。
対象となるテーブルを選択してEntity名を指定したら、テーブルにあるカラムから自動的にEntityのプロパティを作成してくれます。
Entityはsampleプロジェクトのapp/modules/sample_module/entityディレクトリにS2BasePlugin.phpという名前で作成されて、以下の様な内容になっています。
 
<?php
class S2BasePlugin {
    const TABLE = "s2base_plugin";
    public function __construct(){}
 
    protected $id;
    const id_COLUMN = "id";
    public function setId($val){$this->id = $val;}
    public function getId(){return $this->id;}
 
    protected $name;
    const name_COLUMN = "name";
    public function setName($val){$this->name = $val;}
    public function getName(){return $this->name;}
 
 
    public function __toString() {
        $buf = array();
        $buf[] = 'id => ' . $this->getId();
        $buf[] = 'name => ' . $this->getName();
        return '{' . implode(', ',$buf) . '}';
    }
 
 
    /*
    private $prop;
    const prop_RELNO = 0;
    const prop_RELKEYS = 'this_fk:other_pk';
    public function setProp(OtherEntity $entity){ $this->prop = $entity; }
    public function getProp(){ return $this->prop; }
    */
}
 
constで宣言されている箇所がS2Daoの定数アノテーションです。
定数TABLEは、Entityとテーブルを関連付けるTABLEアノテーション
*_COLUMNは、Entityのプロパティとカラムを関連付けるCOLUMNアノテーションです。

Daoの作成

データ型となるEntityができましたので、次にEntityの永続化を行うDaoを作成します。
S2DaoではDaoはInterfaceとして作成し、実装はS2Dao+S2Containerによって自動的に作成されます。
S2BasePluginというEntityに対して、S2BasePluginDaoというDaoを作成します。

 
[ Command list ]
0 : (exit)
1 : dao
2 : dicon
3 : entity
4 : goya
5 : interceptor
6 : module
7 : service
choice ? : 1  <-daoコマンドを表す1を入力してエンターキー
 
[ Module list ]
0 : (exit)
1 : sample_module
choice ? : 1  <-sample_moduleを表す1を入力してエンターキー
 
use database ? (y/n) : y  <-yを入力してエンターキー
 
[ table list ]
0 : (exit)
1 : s2base_plugin
2 : sqlite_sequence
choices ? (1,2,--,) : 1  <-s2base_pluginテーブルを表す1を入力してエンターキー
 
dao interface name [S2base_pluginDao]? : S2BasePluginDao <-Dao名となるS2BasePluginDaoを入力してエンターキー
 
entity class name ? [S2base_pluginEntity] : S2BasePlugin <-Entity名となるS2BasePluginを入力してエンターキー
 
[ generate information ]
  module name          : sample_module
  dao interface name   : S2BasePluginDao
  dao test class name  : S2BasePluginDaoTest
  entity class name    : S2BasePlugin
  table name           : s2base_plugin
  columns              : id, name
 
confirm ? (y/n) : y  <-yを入力してエンターキー
 
で、ここまで実行してまぬけな事態に気づく・・・。
DaoコマンドってEntityも作ってくれるんでした・・・^^;
前に実行したEntityコマンドって意味なかったですね・・・^^;;
えー、気を取り直して。
daoコマンドを実行すると、
DaoのInterfaceがapp/modules/sample_module/daoディレクトリに
Daoのテストクラスがtest/modules/sample_module/daoディレクトリに
そしてEntityがapp/modules/sample_module/entityディレクトリに作られます。
S2BasePluginDaoはこんな風になっています。
 
<?php
interface S2BasePluginDao {
    const BEAN = "S2BasePlugin";
 
    public function findAllList();
    //public function findAllArray();
    //public function update(S2BasePlugin $entity);
    //public function insert(S2BasePlugin $entity);
    //public function delete(S2BasePlugin $entity);
}
 
S2Daoでは、メソッドに規則に沿った名前をつけることで、メソッドの実体が作られます。
例えばS2BasePluginDaoのfindAllListは、更新系の命名規則になっていなくて
Listという文字でメソッド名が終わっていますので
検索メソッドとして実体が作られて、戻り値がS2Dao_ArrayList形式で戻ります。
この状態では、検索条件も指定されていないので
 
SELECT id, name FROM s2base_plugin;
 
というSQLが実行された結果がS2BasePluginを格納したS2Dao_ArrayListとして帰ってくることになります。
このあたりはS2Daoのことになりますから、S2Daoをなでる時にでもまた少し。
今回は更新系の処理も行いたいので、以下の様にS2BasePluginDaoを変更します。
 
<?php
interface S2BasePluginDao {
    const BEAN = "S2BasePlugin";
 
    public function findAllList();
    public function update(S2BasePlugin $entity);
    public function insert(S2BasePlugin $entity);
    public function delete(S2BasePlugin $entity);
}
 

で、少し補足情報です。
前回作成されたsample_moduleの定義ファイル「sample_module.inc.php」に、
以下の様な記載があります。

 
S2ContainerApplicationContext::import(S2BASE_PHP5_ROOT . '/app/commons/dicon/dao.dicon');
S2ContainerApplicationContext::registerAspect('/Dao$/', 'dao.interceptor');
 
S2DaoでDaoの機能を使用する為には、S2DaoInterceptorというInterceptorをDaoに対して適用する必要があります。
このS2DaoInterceptorが、実体の定義されていないInterfaceであるDaoに対して、メソッドの実装を提供するのです。
つまりいくらS2Daoの規則に沿ったDaoのInterfaceを定義しても、S2DaoInterceptorを適用しないとDaoとして動作しません。
というか実体がないんですから、S2Containerから取得できませんね。
実体を作ってないんですから、少し考えればわかりますよね。
フレームワークのお約束の手順をおまじないにしないことが、フレームワークをきちんと理解する第一歩だと思います。
・・・Delfinoごときがこういうことを言うのもなんですが^^;

さて、話は戻りますがS2ContainerApplicationContext::registerAspectです。
S2Containerの自動アスペクトという機能を使って
Daoという文字で終わる名前のコンポーネントに対して、dao.interceptorを適用しています。
dao.interceptorは、/app/commons/dicon/dao.diconで定義されているS2DaoInterceptorです。
S2Base1.0系では作成したDaoを定義したdiconでS2DaoInterceptorを適用していたのですが、
S2Base2.0系では、S2ContainerApplicationContextでimportしたクラスのうち
Daoという文字で終わるクラスに自動アスペクトでS2DaoInterceptorを適用しています。
これでdiconなしにDaoを定義できるのですが、
逆をかえせばDaoという文字で終わらないDaoを定義すると動作しないので注意です。
Daoという文字で終わらないDaoを定義したなら、diconファイルでアノテーションでS2DaoInterceptorを適用する必要があります。

今日はここまで

なんか無駄に冗長なことをしてしまいましたが、S2Base1.0系とS2Base2.0系の違いが少しでてきました。
次回はDaoのテストクラスを使ってUnitTestしてみます。

Category: PHP | Posted on: 2007/09/27 13:45 | Viewed: 3065

Comments

No comments yet

Add Comment

:

:
:

TrackBacks

このエントリにトラックバックはありません

TrackBack URL

http://www.azul.systems-noel.jp/trackback/item_67.html