Zend_Db_Tableをもっと手軽に使う (コード解説編) #ZFhimekuri

[Zend Framework 日めくりカレンダー2011](http://atnd.org/events/23579)の7日目、「間に合ってない」ってツッコミには土下座しつつ、[Zend\_Db\_Tableをもっと手軽に使う(概要編) #ZFhimekuri](http://qiita.com/items/1539)の続き。
[Githubに上げたサンプルコード](https://github.com/hirak/performance/tree/DbTable_test)の解説を書きます。

趣旨は「とにかくユルく、ActiveRecord風に`Zend_Db_Table`を使う」です。

## 1) 初期化はapplication.iniに。

データベースの接続先やパスワードをどこに書くかは開発者の悩みの種です。
素直に`Zend_Application`をベースにしているアプリの場合は、application.iniに設定を書くだけで`Zend_Db`を初期化できます。
実際に使用するまでDBに接続しないので、実際にDBを使わないページが多くても安心です。

```ini:application/configs/application.ini
[production]
; ...
resources.db.adapter = "Pdo_Sqlite"
resources.db.params.dbname = APPLICATION_PATH "/../data/db/performance.db"
; ...
```

パスワードを直接埋め込むのはセキュリティの意識としてあまりよろしくないので、必要なら設定ファイルを分割するといいです。
参考：[DBのパスワードをapplication.iniから取り除く](http://blog.tojiru.net/article/230726490.html)

`Zend_Db_Table`を使うのであれば特に気にせずnewすればいいですが、直接`Zend_Db`を使いたいときはBootstrap経由で取得する必要があります。

```php:Zend_Db_Adapterを直接使いたいとき
<?php
class HogeController extends Zend_Controller_Action
{
  public function hogeAction() {
    $db = $this->getInvokeArg('bootstrap')->db;

    //アダプターを使ってPDOのようにSQLを直接書いたり色々
    // ...
  }
  //...
}
```

## 2) DBのテーブル定義もConfigfileに。Zend\_Db\_Table\_Definitionを利用する

[公式のQuickStart](http://framework.zend.com/manual/ja/learning.quickstart.create-model.html)ではテーブル定義を`Zend_Db_Table`をextendsしたクラス(Model\_DbTable\_XXXX)に直接書いていました。でも、どうせ大した記述量じゃないのにテーブル一つ一つに対してクラスを作るなんて面倒ですよね。。
`Zend_Db_Table_Definition`を使うと、テーブル定義をひとまとめに出来ます。
http://framework.zend.com/manual/ja/zend.db.table.definition.html

設定ファイルは、`Zend_Config`を使ったほうがいいのですが、面倒なので素のPHPにしてしまいましょう。配列をreturnしているだけのPHPファイルでスキーマ定義を書きます。

```php:application/configs/schema.php
<?php
return array(
    /**
     * postsテーブルスキーマ定義
     *
     */
    'posts' => array(
        'name' => 'posts',  //DB上の実際のテーブル名
        'primary' => 'id',
        'rowClass' => 'Model_Post',
    ),
    //テーブルが他にあれば同じように追加していく
);
```

これで、スキーマの定義を一箇所にまとめることが出来ました。あとは、テーブル定義を取得するには以下のコードを実行します。

```php:フレームワーク中なら任意の位置で書けるコード
<?php
$config = include APPLICATION_PATH . '/configs/schema.php';
$schema = new Zend_Db_Table_Definition($config);
$postsTable = new Zend_Db_Table('posts', $schema);

//$postsTableを使って処理を色々
```


## 3) テーブル取得はグローバル関数にしてしまえ

たくさん作る必要があったクラスをまとめて消去できましたが、テーブル取得のコードが長いですね。
「ゆるく作る」が今回のコンセプトなので、グローバル関数にしてしまいましょう。

library/functions.phpにAR()という関数を定義しておき、public/index.phpでincludeしておきます。

```php:library/functions.php
<?php
function AR($name) {
    static $schema, $cache = array();
    if (isset($cache[$name])) {
        return $cache[$name];
    }
    if (!isset($schema)) {
        $config = require APPLICATION_PATH . '/configs/schema.php';
        $schema = new Zend_Db_Table_Definition($config);
    }

    return $cache[$name] = new Zend_Db_Table($name, $schema);
}

```

```php:public/index.php
<?php
// ...(中略) ...

require_once 'functions.php';

// ...(後略) ...
```

これで、テーブルの取得はAR('posts')のような短いコードで出来るようになります。


## 4) Tableを拡張するのではなくRowを拡張する

用語解説で「Zend\_Db\_TableはActiveRecordでない」と書きましたが、`Zend_Db_Table_Row`を拡張すればかなりActiveRecordに近づけることが可能です。QuickStartの書き方が理想的なのですが、もっとカジュアルに書きたい場合はActiveRecord風にした方が楽だと思います。

`Zend_Db_Table_Row`をextendsしてActiveRecord的ないわゆる「モデル」を作ります。今回の例だと`Model_Post`クラスになります。
モデルはZendFramework標準状態なら「Model\_」から名前を始めるようにし、modelsディレクトリに配置しておけば、自動で読み込まれます。
作ったクラスは`Zend_Db_Table_Definition`に「rowClass」として設定しておくと、通常は`Zend_Db_Table_Row`が使われるところを自作のクラスに差し替えたことになります。

```php:application/models/Post.php
<?php

class Model_Post extends Zend_Db_Table_Row_Abstract
{
    //modifiedを自動更新するよう、save()をオーバーライド
    function save() {
        $this->modified = date('c');
        return parent::save();
    }

    //モデル固有のメソッドを追加
    function sayHello() {
        echo 'Hello ' . __CLASS__;
    }
}

```

あとはPostモデルにバリデーションロジックを増やしたり、get/setの際に自動変換を割りこませたり、色々機能を増やしてぶくぶく太らせていけばいいわけです。

具体的な利用イメージは[Zend\_Db\_Tableをもっと手軽に使う(概要編) #ZFhimekuri](http://qiita.com/items/1539)を参照してください。

かっちり作るもよし、この程度のユルさで作るもよし。`Zend_Db_Table`はそれだけ拡張性が考えられたコンポーネントなのです。