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

    <title>Zend_Db_Table_Rowset</title>

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

        <title>導入</title>

        <para>
            テーブルクラスに対して <code>find()</code>
            あるいは <code>fetchAll()</code> メソッドでクエリを実行すると、
            返される結果は <classname>Zend_Db_Table_Rowset_Abstract</classname> 型のオブジェクトとなります。
            行セットには、<classname>Zend_Db_Table_Row_Abstract</classname> を継承したオブジェクトが含まれます。
            行セットを使用して各行オブジェクトに対して順にアクセスし、
            行のデータを読み込んだり変更したりすることができます。
        </para>

    </sect2>

    <sect2 id="zend.db.table.rowset.fetch">

        <title>行セットの取得</title>

        <para>
            <classname>Zend_Db_Table_Abstract</classname> には <classname>find()</classname> と
            <code>fetchAll()</code> というメソッドがあります。
            これらはどちらも <classname>Zend_Db_Table_Rowset_Abstract</classname> 型のオブジェクトを返します。
        </para>

        <example id="zend.db.table.rowset.fetch.example">

            <title>行セットの取得の例</title>

            <programlisting role="php"><![CDATA[
$bugs   = new Bugs();
$rowset = $bugs->fetchAll("bug_status = 'NEW'");
]]>
            </programlisting>

        </example>

    </sect2>

    <sect2 id="zend.db.table.rowset.rows">

        <title>行セットからの行の取得</title>

        <para>
            通常は、行セットそのものよりもその中に含まれる行のほうが重要になります。
            この節では、行セットを構成する行の情報を取得する方法について説明します。
        </para>

        <para>
            正しい形式のクエリであっても、結果がゼロ行となることがありえます。
            たとえば、抽出条件に一致する行がデータベース内に存在しない場合などです。
            したがって、行セットオブジェクトの中身の行オブジェクトの個数がゼロになることもあります。
            <classname>Zend_Db_Table_Rowset_Abstract</classname> は
            <code>Countable</code> インターフェイスを実装しているので、
            <code>count()</code> を使用すると行セット内の行の数を調べられます。
        </para>

        <example id="zend.db.table.rowset.rows.counting.example">

            <title>行セット内の行の数を数える</title>

            <programlisting role="php"><![CDATA[
$rowset   = $bugs->fetchAll("bug_status = 'FIXED'");

$rowCount = count($rowset);

if ($rowCount > 0) {
    echo "見つかった行数は $rowCount です";
} else {
    echo 'クエリにマッチする行がありません';
}
]]>
            </programlisting>

        </example>

        <example id="zend.db.table.rowset.rows.current.example">

            <title>行セットからの単一の行の読み込み</title>

            <para>
                行セットから行にアクセスするための一番簡単な方法は
                <code>current()</code> メソッドを使用することです。
                これは、行セットに含まれる行数がひとつである場合に最適です。
            </para>

            <programlisting role="php"><![CDATA[
$bugs   = new Bugs();
$rowset = $bugs->fetchAll("bug_id = 1");
$row    = $rowset->current();
]]>
            </programlisting>

        </example>

        <para>
            行セットに含まれる行数がゼロの場合、<code>current()</code>
            が返す値は PHP の <code>null</code> 値となります。
        </para>

        <example id="zend.db.table.rowset.rows.iterate.example">

            <title>行セットの順次処理</title>

            <para>
                <classname>Zend_Db_Table_Rowset_Abstract</classname> を継承したオブジェクトは
                <code>SeekableIterator</code> インターフェイスを実装しています。つまり、
                <code>foreach</code> ループを使用できるということです。
                これを使用して取得した個々の値は <classname>Zend_Db_Table_Row_Abstract</classname>
                オブジェクトとなり、これがテーブルの各行に対応します。
            </para>

            <programlisting role="php"><![CDATA[
$bugs = new Bugs();

// テーブルのすべてのレコードを取得します
$rowset = $bugs->fetchAll();

foreach ($rowset as $row) {

    // 出力は 'Zend_Db_Table_Row' あるいはそれに似たものとなります
    echo get_class($row) . "\n";

    // 行のカラムを読み込みます
    $status = $row->bug_status;

    // 現在の行のカラムの値を変更します
    $row->assigned_to = 'mmouse';

    // 変更をデータベースに書き出します
    $row->save();
}
]]>
            </programlisting>

        </example>

         <example id="zend.db.table.rowset.rows.seek.example">

            <title>行セット内の既知の位置への移動</title>

            <para>
                <code>SeekableIterator</code> は、
                イテレータ内の特定の位置に移動することができます。
                そのために使用するのが <code>seek()</code> メソッドです。
                行番号を渡すと、行セット内のその次の位置に移動することができます。
                行番号は 0 から始まることに注意しましょう。
                インデックスが間違っている場合、あるいは存在しない場合は例外がスローされます。
                <code>count()</code> を使って結果の行数を確認してから移動するようにしましょう。
            </para>

            <programlisting role="php"><![CDATA[
$bugs = new Bugs();

// テーブルのすべてのレコードを取得します
$rowset = $bugs->fetchAll();

// イテレータを 9 番目の要素に移動します (最初の要素がゼロです)
$rowset->seek(8);

// それを取得します
$row9 = $rowset->current();

// そして使用します
$row9->assigned_to = 'mmouse';
$row9->save();
]]>
            </programlisting>

        </example>

            <para>
                <code>getRow()</code> は、位置がわかっている場合に
                行セット内の特定の行を取得するためのメソッドです。
                しかし、この位置はゼロから数え始めることを忘れないようにしましょう。
                <code>getRow()</code> の最初のパラメータは、位置を表す整数値です。
                2 番目のパラメータはオプションで、boolean 値を指定します。
                これは、Rowset イテレータも同時にその場所に移動させるのかどうかを表します
                (デフォルトは false です)。このメソッドはデフォルトでは
                <classname>Zend_Db_Table_Row</classname> オブジェクトを返します。
                指定した位置が存在しない場合は例外をスローします。
                例を示します。
            </para>

            <programlisting role="php"><![CDATA[
$bugs = new Bugs();

// テーブルのすべてのレコードを取得します
$rowset = $bugs->fetchAll();

// 9 番目の要素を取得します
$row9->getRow(8);

// そして使用します
$row9->assigned_to = 'mmouse';
$row9->save();
]]>
            </programlisting>



        <para>
            個々の行オブジェクトにアクセスすると、後は
            <xref linkend="zend.db.table.row" /> で説明しているメソッド群を用いて行を操作できます。
        </para>

    </sect2>

    <sect2 id="zend.db.table.rowset.to-array">

        <title>行セットの配列としての取得</title>

        <para>
            行セット内のすべてのデータに対して配列としてアクセスするには、
            行セットオブジェクトの <code>toArray()</code> メソッドを使用します。
            これは、各行単位でひとつの要素となる配列を返します。
            各エントリは連想配列となり、カラム名とその値が関連付けられています。
        </para>

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

            <title>toArray() の使用法</title>

            <programlisting role="php"><![CDATA[
$bugs   = new Bugs();
$rowset = $bugs->fetchAll();

$rowsetArray = $rowset->toArray();

$rowCount = 1;
foreach ($rowsetArray as $rowArray) {
    echo "row #$rowCount:\n";
    foreach ($rowArray as $column => $value) {
        echo "\t$column => $value\n";
    }
    ++$rowCount;
    echo "\n";
}
]]>
            </programlisting>
        </example>

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

    </sect2>

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

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

        <para>
            <classname>Zend_Db_Table_Rowset_Abstract</classname> 型のオブジェクトはシリアライズ可能です。
            個別の行オブジェクトをシリアライズするのと同じような方式で、
            行セットをシリアライズして後ほどそれを復元することができます。
        </para>

        <example id="zend.db.table.rowset.serialize.example.serialize">

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

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

            <programlisting role="php"><![CDATA[
$bugs   = new Bugs();
$rowset = $bugs->fetchAll();

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

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

        </example>

        <example id="zend.db.table.rowset.serialize.example.unserialize">

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

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

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

            <programlisting role="php"><![CDATA[
$rowsetDisconnected = unserialize($serializedRowset);

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

        </example>

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

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

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

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

            <programlisting role="php"><![CDATA[
$rowset = unserialize($serializedRowset);

$bugs = new Bugs();

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

$row = $rowset->current();

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

        </example>

        <para>
            行セットを <code>setTable()</code> で復活させると、
            その中に含まれる行オブジェクトもすべて復活した状態になります。
        </para>

    </sect2>

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

        <title>行セットクラスの拡張</title>

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

        <example id="zend.db.table.rowset.extending.example">
            <title>独自の行セットクラスの指定</title>
            <programlisting role="php"><![CDATA[
class MyRowset extends Zend_Db_Table_Rowset_Abstract
{
    // ...独自の処理
}

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

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

        <para>
            一般的には、標準の具象クラス <classname>Zend_Db_Rowset</classname>
            でたいていの場合は十分でしょう。しかし、
            特定のテーブルに固有の処理を行セットに追加したくなることもあります。
            たとえば、行セット内のすべての行の内容の集計用のメソッドなどです。
        </para>

        <example id="zend.db.table.rowset.extending.example-aggregate">
            <title>行セットクラスに新しいメソッドを追加する例</title>
            <programlisting role="php"><![CDATA[
class MyBugsRowset extends Zend_Db_Table_Rowset_Abstract
{
    /**
     * 現在の行セットのなかで、'updated_at' カラムの値が
     * 最大である行を見つけます
     */
    public function getLatestUpdatedRow()
    {
        $max_updated_at = 0;
        $latestRow = null;
        foreach ($this as $row) {
            if ($row->updated_at > $max_updated_at) {
                $latestRow = $row;
            }
        }
        return $latestRow;
    }
}

class Bugs extends Zend_Db_Table_Abstract
{
    protected $_name = 'bugs';
    protected $_rowsetClass = 'MyBugsRowset';
}
]]>
            </programlisting>
        </example>

    </sect2>


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