<sect1 id="zend.db.table.row">

    <title>Zend_Db_Table_Row</title>

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

        <title>Введение</title>

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

        <para>
            Zend_Db_Table_Row является реализацией паттерна
            <ulink url="http://www.martinfowler.com/eaaCatalog/rowDataGateway.html">Row Data Gateway</ulink>.
        </para>

    </sect2>

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

        <title>Извлечение строки</title>

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

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

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

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

        </example>

        <para>
            Объект Zend_Db_Table_Rowset содержит коллекцию объектов
            Zend_Db_Table_Row. Для получения более подробной информации
            читайте <xref linkend="zend.db.table.rowset" />.
        </para>

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

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

            <programlisting role="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>
                Zend_Db_Table_Row_Abstract предоставляет методы-аксессоры,
                благодаря которым можно ссылаться на столбцы в строке как на
                свойства объекта.
            </para>

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

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

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

// Вывод значения столбца bug_description
echo $row->bug_description;
]]>
                </programlisting>

            </example>

            <note>

                <para>
                    Более ранние версии Zend_Db_Table_Row сопоставляли
                    аксессоры столбцов и имена столбцов в БД с использованием
                    преобразования строк, называемым
                    <emphasis>инфлекцией</emphasis>.
                </para>

                <para>
                    Zend_Db_Table_Row в его текущей реализации не использует
                    инфлекцию. Написание аксессоров столбцов должно в точности
                    соответствовать именам столбцов, так, как они представлены в
                    БД.
                </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 role="php"><![CDATA[
$bugs = new Bugs();
$row = $bugs->fetchRow($bugs->select()->where('bug_id = ?', 1));

// Получение ассоциативного массива столбцов и их значений из объекта Row
$rowArray = $row->toArray();

// Теперь используется как обычный массив
foreach ($rowArray as $column => $value) {
    echo "Column: $column\n";
    echo "Value:  $value\n";
}
]]>
                </programlisting>

            </example>

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

        </sect3>

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

            <title>Извлечение данных из связанных таблиц</title>

            <para>
                Класс Zend_Db_Table_Row_Abstract предоставляет методы для
                извлечения строк и наборов строк из связанных таблиц.
                Читайте <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 role="php"><![CDATA[
$bugs = new Bugs();
$row = $bugs->fetchRow($bugs->select()->where('bug_id = ?', 1));

// Изменение значения одного или более столбцов
$row->bug_status = 'FIXED';

// Обновление строки в БД с новыми значениями
$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 role="php"><![CDATA[
$bugs = new Bugs();
$newRow = $bugs->createRow();

// Установка значений столбцов
$newRow->bug_description = '...description...';
$newRow->bug_status = 'NEW';

// Вставка новой строки в БД
$newRow->save();
]]>
                </programlisting>
            </example>

            <para>
                Опциональный аргумент метода является ассоциативным массивом,
                через который вы можете заполнить поля новой строки.
            </para>

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

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

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

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

// вставка новой строки в БД
$newRow->save();
]]>
                </programlisting>

            </example>

            <note>
                <para>
                    В более ранних релизах Zend_Db_Table метод
                    <code>createRow()</code> назывался <code>fetchNew()</code>.
                    Мы рекомендуем использовать новое имя метода,
                    несмотря на то, что старое имя метода по-прежнему работает
                    в целях обеспечения обратной совместимости.
                </para>
            </note>

        </sect3>

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

            <title>Изменение значений в нескольких столбцах</title>

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

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

                <title>Пример использования метода setFromArray() для установки
                значений в новой строке</title>

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

// Данные помещаются в ассоциативный массив
$data = array(
    'bug_description' => '...description...',
    'bug_status'      => 'NEW'
);

// Одновременная установка значений всех столбцов
$newRow->setFromArray($data);

// Добавление новой строки в БД
$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 role="php"><![CDATA[
$bugs = new Bugs();
$row = $bugs->fetchRow('bug_id = 1');

// Удаление строки
$row->delete();
]]>
                </programlisting>

            </example>

            <para>
                Не нужно вызывать метод <code>save()</code> для фиксации
                удаления, оно сразу выполняется в БД.
            </para>

        </sect3>

    </sect2>

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

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

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

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

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

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

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

                <title>Пример сериализации объекта строки</title>

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

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

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

                <title>Пример десериализации объекта строки</title>

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

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

            </example>

            <note>

                <title>Почему объекты строки десериализуются без соединения?</title>

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

            </note>

        </sect3>

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

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

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

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

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

                <programlisting role="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>
            Zend_Db_Table_Row является используемым по умолчанию классом,
            который наследует от Zend_Db_Table_Row_Abstract. Вы можете
            определить свой собственный класс для экземпляров строк путем
            наследования от Zend_Db_Table_Row_Abstract. Для того, чтобы этот
            класс использовался для хранения результатов запросов к таблице,
            укажите его имя в защищенном свойстве <code>$_rowClass</code>
            класса таблицы или в массиве, передаваемом в качестве аргумента
            конструктору объекта таблицы.
        </para>

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

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

            <programlisting role="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 role="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</title>

            <para>
                Класс строки вызывает защищенные методы <code>_insert()</code>,
                <code>_update()</code> и <code>_delete()</code> до выполнения
                соответствующих операций <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>
                    Вам нужно, чтобы операции над данными в этой таблице
                    журналировались через объект Zend_Log, но только если в
                    конфигурации приложения включено это поведение.
                </para>

                <programlisting role="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 role="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>
                Некоторые разработчики предпочитают, чтобы имя класса таблицы
                соответствовало имени таблицы в СУРБД с применением
                преобразования, называемого <emphasis>инфлекцией</emphasis>.
            </para>

            <para>
                Классы Zend_Db по умолчанию не производят
                инфлекцию. Читайте
                <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 role="php"><![CDATA[
class MyInflectedRow extends Zend_Db_Table_Row_Abstract
{
    protected function _transformColumn($key)
    {
        $nativeKey = myCustomInflector($key);
        return $nativeKey;
    }
}

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

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

// Используются имена столбцов в формате CamelCase, преобразующая функция
// изменяет их представление на "родное"
$row->bugDescription = 'New description';
]]>
                </programlisting>

            </example>

            <para>
                Реализация функций для произведения инфлекционного
                преобразования возлагается на разработчика. Zend Framework не
                предоставляет для этих целей готовых функций.
            </para>

        </sect3>

    </sect2>

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