<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>, результат
            возвращается в объекте типа
            <code>Zend_Db_Table_Rowset_Abstract</code>. Набор
            строк (rowset) содержит коллекцию объектов, наследующих от
            <code>Zend_Db_Table_Row_Abstract</code>. Вы можете
            производить итерацию по набору строк и работать с отдельными
            объектами строк, считывая или изменяя данные в строках.
        </para>

    </sect2>

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

        <title>Получение набора строк</title>

        <para>
            <code>Zend_Db_Table_Abstract</code> предоставляет методы
            <code>find()</code> и <code>fetchAll()</code>, оба они возвращают
            объект типа <code>Zend_Db_Table_Rowset_Abstract</code>.
        </para>

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

            <title>Пример извлечения набора строк</title>

            <programlisting language="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>
            Правильно составленный запрос возвращает нулевое количество строк,
            если в БД нет строк, соответствующих условиям запроса. Поэтому
            объект набора строк может содержать нулевое количество объектов
            строк. Поскольку <code>Zend_Db_Table_Rowset_Abstract</code>
            реализует инерфейс <code>Countable</code>, то вы можете использовать
            <code>count()</code> для определения количества строк в наборе
            строк.
        </para>

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

            <title>Подсчет количества строк в наборе строк</title>

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

$rowCount = count($rowset);

if ($rowCount > 0) {
    echo "found $rowCount rows";
} else {
    echo 'no rows matched the query';
}
]]>
            </programlisting>

        </example>

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

            <title>Чтение одной строки из набора строк</title>

            <para>
                Наиболее простой способ получения доступа к строке из набора
                состоит в использовании метода <code>current()</code>. Этот
                способ особенно подходит для тех случаев, когда набор строк
                содержит только одну строку.
            </para>

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

        </example>

        <para>
            Если набор строк содержит нулевое количество строк, то
            <code>current()</code> вернет значение <constant>NULL</constant>.
        </para>

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

            <title>>Итерация по набору строк</title>

            <para>
                Объекты, наследующие от
                <code>Zend_Db_Table_Rowset_Abstract</code>, реализуют интерфейс
                <code>Iterator</code>. Это значит, что можно производить
                циклический обход объектов, используя конструкцию
                <code>foreach()</code>. Каждое значение, получаемое таким
                образом, является объектом типа
                <code>Zend_Db_Table_Row_Abstract</code>, который соответствует
                одной записи в таблице.
            </para>

            <programlisting language="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>

            <para>
                <code>getRow()</code> позволяет извлекать строку из
                набора строк, используя номер ее позиции, при этом не забывайте,
                что отсчет позиции начинается с нуля. Первый параметр метода
                <code>getRow()</code> должен быть целочисленным и используется
                для передачи позиции строки. Второй
                необязательный параметр должен иметь булев тип, при передаче
                значения true итератор перемещается в данную позицию
                (по умолчанию используется значение false). По умолчанию
                этот метод возвращает объект Zend_Db_Table_Row. Если запрошенная
                позиция не существует, то бросается исключение. Ниже приведен
                пример:
            </para>

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

// извлечение всех строк из таблицы
$rowset = $bugs->fetchAll();

// извлечение девятой строки:
$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 language="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>
            Объекты типа <code>Zend_Db_Table_Rowset_Abstract</code> доступны для
            сериализации. Сериализация производится так же, как и для отдельных
            объектов строк - вы можете сериализовать объект набора строк и
            восстановить его позднее.
        </para>

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

            <title>Сериализация набора строк</title>

            <para>
                Используйте PHP-функцию <code>serialize()</code> для
                получения строки, содержащей представление объекта набора строк
                в виде последовательности байт.
            </para>

            <programlisting language="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>
                Используйте функцию <code>unserialize()</code> для
                восстановления из строки, содержащей представление объекта в
                виде последовательности байт. Эта функция возвращает исходный
                объект.
            </para>

            <para>
                Внимание: объект набора строк возвращается <emphasis>без
                соединения с БД</emphasis>. Вы можете производить циклический
                обход набора строк, читать объекты строк и их свойства, но не
                изменять значения в строках или выполнять другие методы,
                требующие соединения с БД (например, запросы к связанным
                таблицам).
            </para>

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

// Теперь вы можете использовать свойства и методы объекта, но только для чтения
$row = $rowsetDisconnected->current();
echo $row->bug_description;
]]>
            </programlisting>

        </example>

        <note>
            <title>Почему объекты наборов строк десериализируются без соединения с БД?</title>
            <para>
                Сериализованный объект является строкой, которая доступна
                для чтения всем, кто ею обладает.
                Это создает угрозу безопасности, которая состоит в
                том, что в сериализованной строке сохраняются такие
                параметры, как логин и пароль для соединения с БД, в
                незашифрованном виде.
                Для вас может быть нежелательным сохранять такие данные в
                незащищенном текстовом файле, отправлять его через e-mail
                или любой другой носитель, который может быть прочитан
                потенциальным атакующим.
                Тот, кто прочитает сериализованный объект, не должен иметь
                возможности использовать его для получения
                несанкционированного доступа к БД.
            </para>
        </note>

        <para>
            Вы можете восстановить соединение для набора строк, используя
            метод <code>setTable()</code>. Аргументом этого метода является
            объект типа <code>Zend_Db_Table_Abstract</code>, который создается
            вами. Создание объекта таблицы требует действующего соединения с БД,
            поэтому при переустановке таблицы объект набора строк получает
            доступ к БД. После этого вы сможете изменять значения объектов
            строк, содержащихся в объекте набора строк и сохранять изменения
            в БД.
        </para>

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

            <title>Пример восстановления соединения для набора строк</title>

            <programlisting language="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>
            Вы можете использовать альтернативный класс для наборов
            строк путем расширения класса Zend_Db_Table_Rowset_Abstract.
            Указывайте новый класс набора строк через имя в защищенном свойстве
            <varname>$_rowsetClass</varname> класса таблицы или в массиве,
            передаваемом в качестве аргумента конструктору объекта таблицы.
        </para>

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

            <title>Указание своего класса набора строк</title>

            <programlisting language="php"><![CDATA[
class MyRowset extends Zend_Db_Table_Rowset_Abstract
{
    // ...кастомизация
}

// Укажите свой класс набора строк в качестве используемого по умолчанию
// во всех экземплярах класса таблицы
class Products extends Zend_Db_Table_Abstract
{
    protected $_name = 'products';
    protected $_rowsetClass = 'MyRowset';
}

// Или укажите свой класс набора строк для использования
// в конкретном экземпляре класса таблицы
]]>
            </programlisting>

        </example>

        <para>
            Стандартный класс Zend_Db_Rowset подходит для большинства случаев
            использования. Тем не менее, может потребоваться добавить свою
            логику в набор строк, специфический для данной таблицы.
            Например, новый метод может вычислять агрегированное значение для
            всех строк в наборе.
        </para>

        <example id="zend.db.table.rowset.extending.example-aggregate">

            <title>Пример класса набора строк с новым методом</title>

            <programlisting language="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:
-->
