9.5. Zend_Db_Table_Row

9.5.1. Введение

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

Zend_Db_Table_Row является реализацией паттерна Row Data Gateway.

9.5.2. Извлечение строки

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

Пример 9.57. Пример извлечения строки

<?php

$bugs = new Bugs();
$row = $bugs->fetchRow('bug_id = 1');

?>

Объект Zend_Db_Table_Rowset содержит коллекцию объектов Zend_Db_Table_Row. См. Раздел 9.6, «Zend_Db_Table_Rowset».

Пример 9.58. Пример получения строки из набора строк

<?php

$bugs = new Bugs();
$rowset = $bugs->fetchAll("bug_status = 'NEW'");
$row = $rowset->current();

?>

9.5.2.1. Чтение значения столбца из строки

Zend_Db_Table_Row_Abstract предоставляет методы доступа (аксессоры), поэтому можно ссылаться на столбцы в строке как на свойства объекта.

Пример 9.59. Пример чтения столбца в строке

<?php

$bugs = new Bugs();
$row = $bugs->fetchRow('bug_id = 1');

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

?>
[Замечание] Замечание

Более ранние версии Zend_Db_Table_Row сравнивали аксессоры столбцов и имена столбцов в БД с преобразованием строк, называемым инфлекцией.

Теперь Zend_Db_Table_Row не реализует инфлекцию. Написание аксессоров столбцов должно в точности соответствовать именам столбцов, так, как они представлены в БД.

9.5.2.2. Получение данных строки в виде массива

Вы можете получить доступ к данным строки, используя метод toArray() объекта строки. Метод возвращает ассоциативный массив имен столбцов и их значений.

Пример 9.60. Пример использования метода toArray()

<?php

$bugs = new Bugs();
$row = $bugs->fetchRow('bug_id = 1');

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

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

?>

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

9.5.2.3. Извлечение данных из связанных таблиц

Класс Zend_Db_Table_Row_Abstract предоставляет методы для извлечения строк и наборов строк из связанных таблиц. Cм. Раздел 9.7, «Связи между таблицами Zend_Db_Table» для более подробной информации о связях между таблицами.

9.5.3. Редактирование строк в БД

9.5.3.1. Изменение значений столбцов в строке

Используя аксессоры столбцов, вы можете устанавливать значения отдельных столбцов по аналогии с чтением, т.е. так же, как если бы они были свойствами объекта.

Это изменяет значение столбца строки в вашем приложении, но это изменение еще не фиксируется в БД. Вы можете выполнить фиксацию через метод save().

Пример 9.61. Пример изменения значения столбца в строке

<?php

$bugs = new Bugs();
$row = $bugs->fetchRow('bug_id = 1');

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

// Обновление строки в БД с использованием новых значений
$row->save();

?>
[Замечание] Замечание

В настоящее время Zend_Db_Table_Row_Abstract бросает исключение, если вы пытаетесь установить значение столбца первичного ключа. Это поведение планируется изменить в версии 1.0. Класс строки должен позволять устанавливать значения столбцов первичного ключа для поддержки последовательностей и естетственных первичных ключей.

9.5.3.2. Вставка новой строки

Вы можете создавать новые строки для определенной таблицы с помощью метода createRow() класса таблицы. Можно работать с полями этой строки через объектно-ориентированный интерфейс, но строка не сохраняется в БД до тех пор, пока вы не вызовете метод save().

Пример 9.62. Пример создания новой строки таблицы

<?php

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

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

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

?>

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

Пример 9.63. Пример заполнения новой строки для таблицы

<?php

$data = array(
    'bug_description' => '...описание...',
    'bug_status'      => 'NEW'
);

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

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

?>
[Замечание] Замечание

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

9.5.3.3. Изменение значений в нескольких столбцах

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

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

<?php

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

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

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

// Добавление новой строки в БД
$newRow->save();

?>

9.5.3.4. Удаление строки

Вы можете вызывать метод delete() объекта строки. Этот метод удаляет из таблицы строки, соответствующие первичному ключу в объекте строки.

Пример 9.65. Пример удаления строки

<?php

$bugs = new Bugs();
$row = $bugs->fetchRow('bug_id = 1');

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

?>

Вы не должны вызывать save() для фиксации удаления, оно сразу же выполняется в БД.

9.5.4. Сериализация и десериализация строк

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

9.5.4.1. Сериализация строки

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

Пример 9.66. Пример сериализации строки

<?php

$bugs = new Bugs();
$row = $bugs->fetchRow('bug_id = 1');

// Преобразование объекта в сериализованную форму
$serializedRow = serialize($row);

// Теперь вы можете записать $serializedRow в файл и т.д.

?>

9.5.4.2. Десериализация строки

Используйте функцию unserialize() для восстановления из строки, содержащей представление объекта в виде последовательности байт.

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

Пример 9.67. Пример десериализации сериализованной строки

<?php

$rowClone = unserialize($serializedRow);

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

?>
[Замечание] Почему объекты строки десериализуются в состояние без соединения?

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

9.5.4.3. Восстановление соединения для строки

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

Пример 9.68. Пример восстановления соединения для строки

<?php

$rowClone = unserialize($serializedRow);

$bugs = new Bugs();

// Привязка строки к таблице с действующим соединением БД
$rowClone->setTable($bugs);

// Теперь вы можете производить изменения в строке и сохранять их
$rowClone->bug_status = 'FIXED';
$rowClone->save();

?>

9.5.5. Расширение класса строки

Вы можете использовать альтернативный класс для экземпляров строк путем расширения класса Zend_Db_Table_Row_Abstract. Указывайте новый класс набора строк через имя в защищенном члене $_rowClass класса таблицы или в массиве, передаваемом в качестве аргуемента конструктору объекта таблицы.

Пример 9.69. Указание своего класса строки в качестве используемого

<?php

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'));

?>

9.5.5.1. Определение собственной логики для добавления, обновления и удаления в Zend_Db_Table_Row

Класс строки вызывает защищенные методы _insert(), _update() и _delete() до выполнения соответствующих операций INSERT, UPDATE и DELETE. Вы можете добавлять собственную логику в эти методы в созданном вами подклассе строки.

Если вам нужно выполнение собственной логики в определенной таблице, и эта логика должна выполняться для каждой операции в этой таблице, то разумным решением может быть реализация собственной логики внутри методов insert(), update() и delete() вашего класса таблицы. Тем не менее, иногда может быть необходимым выполнять собственную логику в классе строки.

Ниже приведены примеры некоторых случаев, в которых имеет смысл реализовать свою логику в классе строки вместо класса таблицы:

Пример 9.70. Пример собственной логики в классе строки

Собственная логика может применяться не во всех случаях операций над определенной таблицей. Вы можете реализовать свою логику в классе строки и создавать экземпляр класса таблицы с указанием этого класса строки в качестве используемого. Иначе в объекте таблицы используется класс строки по умолчанию.

Вам нужно, чтобы операции над данными в этой таблице журналировались через объект Zend_Log, но только если в конфигурации приложения включено это поведение.

<?php

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();
}

?>

Пример 9.71. Пример класса строки, журналирующего добавляемые данные для множества таблиц

Собственная логика может быть общей для множества таблиц. Вместо реализации одной и той же логики в каждом из классов таблиц вы можете реализовать код для каждого действия в классе строки и использовать этот класс строки во всех ваших классах таблиц.

В этом примере журналирующий код одинаков для всех классов таблиц.

<?php

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';
}

?>

9.5.5.2. Определение инфлекции в Zend_Db_Table_Row

Некоторые разработчики предпочитают, чтобы имя класса таблицы сопоставлялось с именем таблицы в СУРБД с применением преобразования, называемой инфлекцией.

Классы Zend_Db по умолчанию не производят инфлекцию. Причины такого решения приведены в Раздел 9.4.12.4, «Define Inflection in Zend_Db_Table».

Если вы предпочитаете использовать инфлекцию, то должны сами реализовать преобразование, переопределив метод _transformColumn() в своем классе строки и использовать этот класс при произведении запросов через ваш класс таблицы.

Пример 9.72. Пример определения инфлекционного преобразования

Это позволяет использовать в аксессорах преобразованную версию имени столбца. Класс строки использует метод _transformColumn() для преобразования имени, используемого в качестве "родного" в таблице БД.

<?php

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';

?>

Вы отвественны за написание функций для выполнения инфлекционного преобразования. Zend Framework не предоставляет таких функций.