5.10. ERDデザインツールとS2Erd

S2Erdは、ERDデザインツールのデータからZend_Db_TableクラスのPHPソースを生成します。

5.10.1. 動作環境

  • PHP-5.3.2
  • Zend Framework v1.10.2
  • s2container.php-2.0.3
  • s2erd-0.3.0

対応しているERDデザインツール


5.10.2. サンプルプロジェクトの作成

Zend_ControllerでS2Containerを使用する」にしたがって、Zend Frameworkのプロジェクトを作成します。


5.10.3. S2Erdのインストール

ダウンロード. 


PEAR インストール. 

S2Erd-version.tgzをダウンロードし、pear install を実行します。

% pear install /path/to/S2Erd-version.tgz
install ok: channel://pear.php.net/S2Erd-version

% pear list 
・・・
S2Erd             version       state
・・・
%

zf.iniファイルの作成. 

ホームディレクトリに次のzf.iniファイルを作成します。

% cat ~/zf.ini
basicloader.classes.0 = "seasar_erd_util_zend_Manifest"
php.include_path = ".:/usr/lib/php:/path/to/zf/project/library"
%

5.10.4. S2Erdの設定

S2Erdの設定を行うs2erd.phpをapplication/configsディレクトリに作成します。

  • application/configs/s2erd.php
    • 使用するERDデザインツール用のParserクラスを選択します。
      Parserクラスへの設定として、ERDデザインツールのデータファイルへのパスを設定します。
    • Zend_Db_Tableクラスのソースを生成するGeneratorクラスの設定として、出力先ディレクトリやTableクラスのテンプレートファイルなどを設定します。

5.10.5. Autoload設定

S2Erd用のオートローダーを設定することで、ERDデザインツールのデータから、直接PHPのTableクラスを定義することができます。
テーブル定義が不定の間は、このオートローダーでTableクラス定義を読み込み、テーブル定義が確定した後は、次の「Zend_Toolコマンド」で Tableクラス定義をファイルに出力します。

s2erd_autoload.phpの作成. 

S2Erd用のAutoload設定を行うs2erd_autoload.phpをapplication/configsディレクトリに作成します。


Bootstrap.phpの編集. 

アプリケーションのBootstrapでs2erd_autoload.phpをrequireします。(_initAutoloadメソッド)


5.10.6. Zend_Toolコマンド

Zend_Tool の zf コマンドを実行し、次のコマンドが追加されているかどうかを確認します。

% zf.sh --help
  . . .
  . . .
  . . .

  A5Model
    zf create a5model a5er-file[=<project/var/db/project.a5er>]

  ERMModel
    zf create erm-model erm-file[=<project/var/db/project.erm>]

  MWBModel
    zf create mwb-model schema[=mydb] mwb-file[=<project/var/db/project.mwb>]

%

使用しているERDデザインツール用のコマンドを実行します。Tableクラス定義ファイルは、application/models/DbTable ディレクトリに出力されます。
zfコマンドのオプションとして「--pretend」を付加した場合は、標準出力にTableクラス定義が出力されます。(ファイル出力されません。)

サンプルとして、var/db ディレクトリに次のERDデザインツールのデータファイルを用意しています。
テーブル定義はZend_Dbドキュメントのサンプルデータベースを参照下さい。

例として、MySQL Workbench(project.mwb)を対象とするMWBModelコマンドを実行してみます。

% ls application/models/DbTable
% zf create mwb-model s2

MySQL Workbench File:   /project/var/db/project.mwb

create : application/models/DbTable/Abstract.php
create : application/models/DbTable/Accounts.php
create : application/models/DbTable/Bugs.php
create : application/models/DbTable/BugsProducts.php
create : application/models/DbTable/Products.php

% cat application/models/DbTable/Bugs.php
<?php
/**
 * @S2Component('autoBinding' => 'none')
 */
class Model_DbTable_Bugs extends Model_DbTable_Abstract {

    const PNAME = 'bugs';
    const LNAME = 'bugs';
    const COMMENT = '';

    public static $FIELDS = array(
        'bug_id' => array(
              'pname' => 'bug_id',
              'lname' => 'bug_id',
              'type' => 'INT',
              'type_opt' => null,
              'length' => -1,
              'scale' => -1,
              'precision' => -1,
              'nn' => true,
              'pk' => true,
              'fk' => false,
              'default' => '',
              'comment' => '',
              'options' => null),

              . . . . . 
              . . . . . 
              . . . . . 

        'verified_by' => array(
              'pname' => 'verified_by',
              'lname' => 'verified_by',
              'type' => 'VARCHAR',
              'type_opt' => null,
              'length' => 100,
              'scale' => -1,
              'precision' => -1,
              'nn' => false,
              'pk' => false,
              'fk' => true,
              'default' => '',
              'comment' => '',
              'options' => null));

    public static $FILTERS = array(
        'bug_id' => array('Int'));

    public static $VALIDATORS = array(
        'bug_id' => array('presence' => 'required', 'Int'),
        'bug_description' => array('allowEmpty' => 'true', array('StringLength', 0, 100, 'UTF-8')),
        'bug_status' => array('allowEmpty' => 'true', array('StringLength', 0, 20, 'UTF-8')),
        'reported_by' => array('allowEmpty' => 'true', array('StringLength', 0, 100, 'UTF-8')),
        'assigned_to' => array('allowEmpty' => 'true', array('StringLength', 0, 100, 'UTF-8')),
        'verified_by' => array('allowEmpty' => 'true', array('StringLength', 0, 100, 'UTF-8')));

    protected $_name = 'bugs';
    protected $_primary = array('bug_id');
    protected $_sequence = false;

    protected $_dependentTables = array('Model_DbTable_BugsProducts');

    protected $_referenceMap = array(
        'ReportedBy' => array(
           'columns' => array('reported_by'),
           'refTableClass' => 'Model_DbTable_Accounts',
           'refColumns' => array('account_name')),
        'AssignedTo' => array(
           'columns' => array('assigned_to'),
           'refTableClass' => 'Model_DbTable_Accounts',
           'refColumns' => array('account_name')),
        'VerifiedBy' => array(
           'columns' => array('verified_by'),
           'refTableClass' => 'Model_DbTable_Accounts',
           'refColumns' => array('account_name')));

}
%

生成されたModel_DbTable_BugsクラスのLNAMEや$FIELDSのlname値は、 A5:SQLやER Masterを使用している場合に日本語カラム名となります。


5.10.7. TableモデルのUnitTest

サンプルとして、accountsテーブルのTableクラスのUnitTestを作成します。UnitTestは、tests/application/models/DbTableディレクトリAccountsTest.php として作成します。

class Model_DbTable_AccountsTest extends PHPUnit_Framework_TestCase {

    public function testFetchAll() {
        $this->assertEquals(5, count($this->model->fetchAll()));
    }

    public function setUp() {
        $this->model = s2get('Model_DbTable_Accounts');
    }

    public function tearDown() {
        $this->model = null;
    }
}

UnitTestの実行結果は次になります。

% phpunit application/models/DbTable
PHPUnit 3.4.2 by Sebastian Bergmann.

.

Time: 1 second

OK (1 test, 1 assertion)
%

Model_DbTable_Accountsクラスは未作成ですが、S2ErdのAutoloadにより動的にクラス定義が実施されるため、 Tableモデルを使用することができます。


5.10.8. ServiceでTableモデルを使用する

サンプルとして、TableモデルやDBアダプタを使用するサービスクラスをapplication/servicesディレクトリSample.php として作成します。

class Service_Sample {

    public function setModel(Model_DbTable_Bugs $model) {
        $this->model = $model;
    }

    public function setAdapter(Zend_Db_Adapter_Abstract $adapter) {
        $this->adapter = $adapter;
    }
    
    public function fetchAllBugs() {
        return $this->model->fetchAll()->toArray();
    }

    public function fetchProductBugDescriptions() {
        $select = $this->adapter->select()->from(array('bp' => Model_DbTable_BugsProducts::PNAME), array());
        $select->joinLeft(array('b' => Model_DbTable_Bugs::PNAME), 'b.bug_id = bp.bug_id', array('bug_description'));
        $select->joinLeft(array('p' => Model_DbTable_Products::PNAME), 'p.product_id = bp.product_id', array('product_name'));
        return $this->adapter->fetchAll($select);
    }
}

TableモデルやDBアダプタはセッターメソッドでインジェクションされます。
検索を行うSQLの発行についてはDBアダプタを利用することが多く、新規作成、更新、削除処理についてはTableモデルを使用します。

SampleサービスクラスのUnitTestを作成します。UnitTestは、tests/application/servicesディレクトリSampleTest.php として作成します。

class Service_SampleTest extends PHPUnit_Framework_TestCase {

    public function testFetchAllBugs() {
        $this->assertEquals(3, count($this->service->fetchAllBugs()));
    }

    public function testFetchAllBugsWithAccounts() {
        $rows = $this->service->fetchProductBugDescriptions();
        $this->assertEquals(6, count($rows));
    }

    public function setUp() {
        require(APPLICATION_PATH . '/dicons/dicon.php');  // 共通設定ファイル
        $this->service = s2get('Service_Sample');
    }

    public function tearDown() {
        $this->service = null;
    }
}

UnitTestの実行結果は次になります。

% phpunit application/services
PHPUnit 3.4.2 by Sebastian Bergmann.

..

Time: 0 seconds

OK (2 tests, 2 assertions)
%

サービスクラスをアクションコントローラで使用する方法については、「アクションメソッドでS2Containerを利用する」 を参照ください。



© Copyright The Seasar Foundation and the others 2005-2010, all rights reserved.