<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<!-- EN-Revision: 15851 -->
<sect1 id="zend.db.table.row">

    <title>Zend_Db_Table_Row</title>

    <sect2 id="zend.db.table.row.introduction">

        <title>導入</title>

        <para>
            <classname>Zend_Db_Table_Row</classname> は、<classname>Zend_Db_Table</classname> オブジェクトの個々の行を含むクラスです。
            テーブルクラスに対してクエリを実行すると、返される結果は
            <classname>Zend_Db_Table_Row</classname> オブジェクトのセットとなります。
            このオブジェクトを使用して新しい行を作成し、
            それをデータベースのテーブルに追加することもできます。
        </para>

        <para>
            <classname>Zend_Db_Table_Row</classname> は、
            <ulink url="http://www.martinfowler.com/eaaCatalog/rowDataGateway.html">
            行データゲートウェイ</ulink>パターンを実装したものです。
        </para>

    </sect2>

    <sect2 id="zend.db.table.row.read">

        <title>行の取得</title>

        <para>
            <classname>Zend_Db_Table_Abstract</classname> は <code>find()</code> や
            <code>fetchAll()</code> といったメソッドを提供します。
            これらはそれぞれ <classname>Zend_Db_Table_Rowset</classname> 型のオブジェクトを返します。
            また <code>fetchRow()</code> メソッドは、
            <classname>Zend_Db_Table_Row</classname> 型のオブジェクトを返します。
        </para>

        <example id="zend.db.table.row.read.example">

            <title>行の取得の例</title>

            <programlisting language="php"><![CDATA[
$bugs = new Bugs();
$row = $bugs->fetchRow($bugs->select()->where('bug_id = ?', 1));
]]></programlisting>

        </example>

        <para>
            <classname>Zend_Db_Table_Rowset</classname> オブジェクトには、複数の
            <classname>Zend_Db_Table_Row</classname> オブジェクトが含まれます。
            <xref linkend="zend.db.table.rowset" /> を参照ください。
        </para>

        <example id="zend.db.table.row.read.example-rowset">

            <title>行セット内の行を読み込む例</title>

            <programlisting language="php"><![CDATA[
$bugs = new Bugs();
$rowset = $bugs->fetchAll($bugs->select()->where('bug_status = ?', 1));
$row = $rowset->current();
]]></programlisting>

        </example>

        <sect3 id="zend.db.table.row.read.get">

            <title>行からのカラムの値の読み込み</title>

            <para>
                <classname>Zend_Db_Table_Row_Abstract</classname> にはアクセサがあり、
                行のカラムをオブジェクトのプロパティとして参照できます。
            </para>

            <example id="zend.db.table.row.read.get.example">

                <title>行からカラムを読み込む例</title>

                <programlisting language="php"><![CDATA[
$bugs = new Bugs();
$row = $bugs->fetchRow($bugs->select()->where('bug_id = ?', 1));

// bug_description カラムの値を出力します
echo $row->bug_description;
]]></programlisting>

            </example>

            <note>

                <para>
                    初期のバージョンの <classname>Zend_Db_Table_Row</classname> では、
                    これらのアクセサをデータベースのカラムと対応させる際に
                    <emphasis>inflection (変形)</emphasis>
                    と呼ばれる文字列変換を行っていました。
                </para>

                <para>
                    現在の <classname>Zend_Db_Table_Row</classname> では変形を実装していません。
                    使用するアクセサ名は、データベース内のカラム名と正確に一致します。
                </para>

            </note>

        </sect3>

        <sect3 id="zend.db.table.row.read.to-array">

            <title>行データの配列としての取得</title>

            <para>
                行のデータに対して配列としてアクセスするには、行オブジェクトの
                <code>toArray()</code> メソッドを使用します。
                これは、カラム名とその値を関連付けた連想配列を返します。
            </para>

            <example id="zend.db.table.row.read.to-array.example">

                <title>toArray() メソッドの使用例</title>

                <programlisting language="php"><![CDATA[
$bugs = new Bugs();
$row = $bugs->fetchRow($bugs->select()->where('bug_id = ?', 1));

// 行オブジェクトから カラム名/値 の連想配列を取得します
$rowArray = $row->toArray();

// 通常の配列と同様に使用します
foreach ($rowArray as $column => $value) {
    echo "カラム: $column\n";
    echo "値:  $value\n";
}
]]></programlisting>

            </example>

            <para>
                <code>toArray()</code> が返す配列は、更新できません。
                配列内の値を変更することは可能ですが、
                それをデータベースに保存することはできません。
            </para>

        </sect3>

        <sect3 id="zend.db.table.row.read.relationships">

            <title>関連するテーブルからのデータの取得</title>

            <para>
                <classname>Zend_Db_Table_Row_Abstract</classname> クラスには、関連するテーブルから
                行や行セットを取得するメソッドが存在します。
                テーブルのリレーションについての詳細な情報は
                <xref linkend="zend.db.table.relationships" /> を参照ください。
            </para>

        </sect3>

    </sect2>

    <sect2 id="zend.db.table.row.write">

        <title>データベースへの行の書き込み</title>

        <sect3 id="zend.db.table.row.write.set">

            <title>行のカラムの値の変更</title>

            <para>
                個々のカラムの値をアクセサで設定する方法は、
                カラムを読み込む場合と同様で、オブジェクトのプロパティを使用します。
            </para>

            <para>
                カラムのアクセサによる値の設定は、アプリケーション内の行データのカラムの値は変更しますが、
                それだけではまだデータベースにコミットされていません。コミットするには
                <code>save()</code> メソッドを使用します。
            </para>

            <example id="zend.db.table.row.write.set.example">

                <title>行のカラムの内容を変更する例</title>

                <programlisting language="php"><![CDATA[
$bugs = new Bugs();
$row = $bugs->fetchRow($bugs->select()->where('bug_id = ?', 1));

// ひとつあるいは複数のカラムの値を変更します
$row->bug_status = 'FIXED';

// データベース内の行を、新しい値で UPDATE します
$row->save();
]]></programlisting>

            </example>

        </sect3>

        <sect3 id="zend.db.table.row.write.insert">

            <title>新しい行の挿入</title>

            <para>
                指定したテーブルに新しい行を作成するには、テーブルクラスの
                <code>createRow()</code> メソッドを使用します。
                取得した行のフィールドに対してはオブジェクト指向のインターフェイスでアクセスできますが、
                <code>save()</code> メソッドをコールするまでは
                実際にデータベースの内容が変更されることはありません。
            </para>

            <example id="zend.db.table.row.write.insert.example">

                <title>テーブルに新しい行を作成する例</title>

                <programlisting language="php"><![CDATA[
$bugs = new Bugs();
$newRow = $bugs->createRow();

// アプリケーションに応じて適切にカラムの値を設定します
$newRow->bug_description = '...説明...';
$newRow->bug_status = 'NEW';

// 新しい行をデータベースに INSERT します
$newRow->save();
]]></programlisting>

            </example>

            <para>
                createRow() メソッドのオプションの引数として、連想配列を渡すことができます。
                この連想配列では、新しい行のフィールドに代入する値を指定します。
            </para>

            <example id="zend.db.table.row.write.insert.example2">

                <title>テーブルに新しい行を作成し、値を代入する例</title>

                <programlisting language="php"><![CDATA[
$data = array(
    'bug_description' => '...説明...',
    'bug_status'      => 'NEW'
);

$bugs = new Bugs();
$newRow = $bugs->createRow($data);

// 新しい行をデータベースに INSERT します
$newRow->save();
]]></programlisting>

            </example>

            <note>

                <para>
                    <classname>Zend_Db_Table</classname> の初期のリリースでは、<code>createRow()</code>
                    メソッドは <code>fetchNew()</code> という名前でした。
                    今後は新しい名前を用いることを推奨しますが、
                    過去との互換性を確保するため古い名前も使用できるようになっています。
                </para>

            </note>

        </sect3>

        <sect3 id="zend.db.table.row.write.set-from-array">

            <title>複数のカラムの値の変更</title>

            <para>
                <classname>Zend_Db_Table_Row_Abstract</classname> の
                <code>setFromArray()</code> メソッドを使用すると、
                ひとつの行の複数のカラムを一度に設定することができます。
                このメソッドには、カラム名と値を関連付けた連想配列を指定します。
                このメソッドは、新しい行の値を設定する場合や
                既存の行を更新する場合のどちらでも有用でしょう。
            </para>

            <example id="zend.db.table.row.write.set-from-array.example">

                <title>setFromArray() で新しい行の値を設定する例</title>

                <programlisting language="php"><![CDATA[
$bugs = new Bugs();
$newRow = $bugs->createRow();

// データを連想配列形式にします
$data = array(
    'bug_description' => '...説明...',
    'bug_status'      => 'NEW'
);

// すべてのカラムの値を一度に設定します
$newRow->setFromArray($data);

// 新しい行をデータベースに INSERT します
$newRow->save();
]]></programlisting>

            </example>

        </sect3>

        <sect3 id="zend.db.table.row.write.delete">

            <title>行の削除</title>

            <para>
                行オブジェクトで <code>delete()</code> メソッドをコールすることができます。
                これは、その行オブジェクトの主キーに対応するデータベースの行を削除します。
            </para>

            <example id="zend.db.table.row.write.delete.example">

                <title>行の削除の例</title>

                <programlisting language="php"><![CDATA[
$bugs = new Bugs();
$row = $bugs->fetchRow('bug_id = 1');

// この行を DELETE します
$row->delete();
]]></programlisting>

            </example>

            <para>
                変更を適用するのに <code>save()</code> をコールする必要はありません。
                これは、データベースに対して即時に適用されます。
            </para>

        </sect3>

    </sect2>

    <sect2 id="zend.db.table.row.serialize">

        <title>行のシリアライズと復元</title>

        <para>
            データベースの行の内容を保存しておき、
            あとで使用するということはよくありがちです。
            オブジェクトの内容を、オフラインで保存しやすい形式 (たとえばファイルなど)
            に変換するような処理のことを <emphasis>シリアライズ</emphasis> といいます。
            <classname>Zend_Db_Table_Row_Abstract</classname> 型のオブジェクトは、
            シリアライズをすることができます。
        </para>

        <sect3 id="zend.db.table.row.serialize.serializing">

            <title>行のシリアライズ</title>

            <para>
                PHP の <code>serialize()</code> 関数を使用して、
                行オブジェクトのバイトストリームを含む文字列を作成します。
            </para>

            <example id="zend.db.table.row.serialize.serializing.example">

                <title>行のシリアライズの例</title>

                <programlisting language="php"><![CDATA[
$bugs = new Bugs();
$row = $bugs->fetchRow('bug_id = 1');

// オブジェクトをシリアライズします
$serializedRow = serialize($row);

// これで、$serializedRow をファイルなどに書き出すことができます
]]></programlisting>

            </example>

        </sect3>

        <sect3 id="zend.db.table.row.serialize.unserializing">

            <title>シリアライズした行データの復元</title>

            <para>
                PHP の <code>unserialize()</code> 関数を使用して、
                オブジェクトのバイトストリームを含む文字列を復元します。
                この関数は、もとのオブジェクトを返します。
            </para>

            <para>
                返された行オブジェクトは、
                <emphasis>接続が切断された</emphasis> 状態であることに注意しましょう。
                行オブジェクトやそのプロパティを読み込むことはできますが、
                その値を変更することはできません。また、データベース接続を必要とするようなメソッド
                (たとえば従属テーブルに対するクエリなど) も実行できません。
            </para>

            <example id="zend.db.table.row.serialize.unserializing.example">

                <title>シリアライズした行の復元の例</title>

                <programlisting language="php"><![CDATA[
$rowClone = unserialize($serializedRow);

// これでオブジェクトのプロパティを使用できますが、読み込み専用です
echo $rowClone->bug_description;
]]></programlisting>

            </example>

            <note>

                <title>復元した行は、なぜ切断された状態なのですか?</title>

                <para>
                    シリアライズしたオブジェクトは、可読形式の文字列となります。
                    データベースのアカウントやパスワードといった情報を
                    暗号化せずにプレーンテキストにシリアライズして保存すると、
                    セキュリティ上問題となります。
                    そのようなデータを無防備な状態でテキストファイルに保存したりしたくはないでしょう。
                    またメールなどで攻撃者に覗き見られることも好まないはずです。
                    シリアライズされたオブジェクトは、
                    正しい認証情報を知らない限りデータベースにアクセスすることはできません。
                </para>

            </note>

        </sect3>

        <sect3 id="zend.db.table.row.serialize.set-table">

            <title>生きたデータとしての行の復活</title>

            <para>
                切断された行の接続を復活させるには、
                <code>setTable()</code> メソッドを使用します。このメソッドへの引数としては、
                <classname>Zend_Db_Table_Abstract</classname> 型のオブジェクトを作成して渡します。
                テーブルオブジェクトを作成するには、データベースとの接続が必要です。
                そのテーブルと行を関連付けることで、行がデータベースにアクセスできるようになります。
                それ以降は、行オブジェクトの値を変更してデータベースに保存できるようになります。
            </para>

            <example id="zend.db.table.row.serialize.set-table.example">

                <title>行の復活の例</title>

                <programlisting language="php"><![CDATA[
$rowClone = unserialize($serializedRow);

$bugs = new Bugs();

// この行をテーブルに再接続し、
// データベースとの接続を復活させます
$rowClone->setTable($bugs);

// これで、行の内容を変更して保存することができます
$rowClone->bug_status = 'FIXED';
$rowClone->save();
]]></programlisting>

            </example>

        </sect3>

    </sect2>

    <sect2 id="zend.db.table.row.extending">

        <title>行クラスの拡張</title>

        <para>
            <classname>Zend_Db_Table_Row</classname> は、<classname>Zend_Db_Table_Row_Abstract</classname>
            を継承したデフォルトの具象クラスです。
            <classname>Zend_Db_Table_Row_Abstract</classname> を継承した具象クラスを新たに作成し、
            それを用いて行のインスタンスを作成することができます。
            独自の行クラスを指定するには、テーブルクラスの protected
            メンバである <varname>$_rowClass</varname> を使用するか、
            テーブルオブジェクトのコンストラクタの引数の配列で指定します。
        </para>

        <example id="zend.db.table.row.extending.example">

            <title>独自の行クラスの指定</title>

            <programlisting language="php"><![CDATA[
class MyRow extends Zend_Db_Table_Row_Abstract
{
    // ...独自の処理
}

// 独自の行を、テーブルクラスの全インスタンスで
// デフォルトとして使用するように設定します
class Products extends Zend_Db_Table_Abstract
{
    protected $_name = 'products';
    protected $_rowClass = 'MyRow';
}

// あるいは、テーブルクラスの特定のインスタンスでのみ
// 独自の行クラスを使用するように設定します
$bugs = new Bugs(array('rowClass' => 'MyRow'));
]]></programlisting>

        </example>

        <sect3 id="zend.db.table.row.extending.overriding">

            <title>行の初期化</title>

            <para>
                行を作成する際にアプリケーション固有のロジックを初期化したい場合は、
                その作業を <code>init()</code> メソッドに移動します。
                このメソッドは、行のメタデータの処理がすべて終わった後にコールされます。
                メタデータを変更するつもりがないのなら、
                <code>__construct</code> メソッドを使うよりもこちらのほうを推奨します。

                <example id="zend.db.table.row.init.usage.example">

                    <title>init() メソッドの使用例</title>

                    <programlisting language="php"><![CDATA[
class MyApplicationRow extends Zend_Db_Table_Row_Abstract
{
    protected $_role;

    public function init()
    {
        $this->_role = new MyRoleClass();
    }
}
]]></programlisting>

                </example>

            </para>

        </sect3>

        <sect3 id="zend.db.table.row.extending.insert-update">

            <title>Zend_Db_Table_Row における Insert、Update および Delete の独自ロジックの定義</title>

            <para>
                行クラスは、<code>INSERT</code> や <code>UPDATE</code>、
                <code>DELETE</code> の操作の前に、対応する protected メソッド
                <code>_insert()</code>、<code>_update()</code>
                および <code>_delete()</code> をコールします。
                行クラスのサブクラスで、これらのメソッドに独自ロジックを追加することができます。
            </para>

            <para>
                特定のテーブルに対して独自のロジックを必要とし、
                それがそのテーブル上のすべての操作に対して発生するのなら、
                その処理はテーブルクラスの
                <code>insert()</code>、<code>update()</code> および
                <code>delete()</code> で実装したほうがよいでしょう。
                しかし、独自のロジックを行クラスで実装したほうがよい場合もあります。
            </para>

            <para>
                独自ロジックの実装を
                テーブルクラスよりも行クラスで行ったほうがよい例を、
                以下にいくつか示します。
            </para>

            <example id="zend.db.table.row.extending.overriding-example1">

                <title>行クラスでの独自ロジックの例</title>

                <para>
                    独自ロジックが、そのテーブルのすべての操作に適用されるとは限りません。
                    状況に応じて独自ロジックを適用するには、
                    そのロジックを行クラスで実装し、
                    その行クラスを指定してテーブルクラスのインスタンスを作成します。
                    指定しなければ、テーブルクラスはデフォルトの行クラスを使用します。
                </para>

                <para>
                    このテーブルでは、データに対する操作内容を <classname>Zend_Log</classname>
                    オブジェクトに記録する必要があります。
                    ただし、それはアプリケーションの設定でログ記録を有効にしている場合のみとします。
                </para>

                <programlisting language="php"><![CDATA[
class MyLoggingRow extends Zend_Db_Table_Row_Abstract
{
    protected function _insert()
    {
        $log = Zend_Registry::get('database_log');
        $log->info(Zend_Debug::dump($this->_data,
                                    "INSERT: $this->_tableClass",
                                    false)
                  );
    }
}

// $loggingEnabled はサンプルとして使用するプロパティで、
// これはアプリケーションの設定によって決まるものとします
if ($loggingEnabled) {
    $bugs = new Bugs(array('rowClass' => 'MyLoggingRow'));
} else {
    $bugs = new Bugs();
}
]]></programlisting>

            </example>

            <example id="zend.db.table.row.extending.overriding-example2">

                <title>挿入するデータの記録を複数のテーブルで行う行クラスの例</title>

                <para>
                    複数のテーブルで、共通の独自ロジックを使用することもあるでしょう。
                    同じロジックをすべてのテーブルクラスで実装するのではなく、
                    その場合はその動作を行クラスで定義しましょう。
                    そして各テーブルでその行クラスを使用するのです。
                </para>

                <para>
                    この例では、ログ記録用のコードは全テーブルクラスで同一です。
                </para>

                <programlisting language="php"><![CDATA[
class MyLoggingRow extends Zend_Db_Table_Row_Abstract
{
    protected function _insert()
    {
        $log = Zend_Registry::get('database_log');
        $log->info(Zend_Debug::dump($this->_data,
                                    "INSERT: $this->_tableClass",
                                    false)
                  );
    }
}

class Bugs extends Zend_Db_Table_Abstract
{
    protected $_name = 'bugs';
    protected $_rowClass = 'MyLoggingRow';
}

class Products extends Zend_Db_Table_Abstract
{
    protected $_name = 'products';
    protected $_rowClass = 'MyLoggingRow';
}
]]></programlisting>

            </example>

        </sect3>

        <sect3 id="zend.db.table.row.extending.inflection">

            <title>Zend_Db_Table_Row における変形の定義</title>

            <para>
                テーブルのクラス名を RDBMS のテーブル名とあわせるために、
                <emphasis>inflection (変形)</emphasis>
                と呼ばれる文字列変換を使用することを好む方もいます。
            </para>

            <para>
                <classname>Zend_Db</classname> クラス群は、デフォルトでは変形をサポートしていません。
                この方針については
                <xref linkend="zend.db.table.extending.inflection" />
                で説明しています。
            </para>

            <para>
                変形をさせたい場合は、変換処理を自前で実装する必要があります。そのためには、
                独自の行クラスで <code>_transformColumn()</code> メソッドをオーバーライドし、
                テーブルクラスでクエリを実行する際にその独自行クラスを使用します。
            </para>

            <example id="zend.db.table.row.extending.inflection.example">

                <title>変換処理の定義例</title>

                <para>
                    これにより、カラム名を変形させたものでアクセスできるようになります。
                    行クラスの <code>_transformColumn()</code>
                    メソッドを使用して、データベースのテーブル内のカラム名を変更しています。
                </para>

                <programlisting language="php"><![CDATA[
class MyInflectedRow extends Zend_Db_Table_Row_Abstract
{
    protected function _transformColumn($columnName)
    {
        $nativeColumnName = myCustomInflector($columnName);
        return $nativeColumnName;
    }
}

class Bugs extends Zend_Db_Table_Abstract
{
    protected $_name = 'bugs';
    protected $_rowClass = 'MyInflectedRow';
}

$bugs = new Bugs();
$row = $bugs->fetchNew();

// キャメルケース形式のカラム名を使用します。
// 変換関数により、これをデータベース内での実際の形式に
// 変換します。
$row->bugDescription = 'New description';
]]></programlisting>

            </example>

            <para>
                変換関数を書くのはあなたの役割です。
                Zend Framework では、そのような関数は用意していません。
            </para>

        </sect3>

    </sect2>

</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
