9.7. Zend_Db_Table_Rowset

9.7.1. Введение

Когда вы производите запрос через класс таблицы, используя методы find() или fetchAll(), результат возвращается в объекте типа Zend_Db_Table_Rowset_Abstract. Rowset (набор строк) содержит коллекцию объектов, наследующих от Zend_Db_Table_Row_Abstract. Вы можете производить итерацию по набору строк и работать с отдельными объектами строк, считывая или изменяя данные в строках.

9.7.2. Получение набора строк

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

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

<?php

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

?>

9.7.3. Получение строк из набора

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

Правильно составленный запрос возвращает нулевое количество строк, если в БД нет строк, соответствующих условиям запроса. Поэтому объект набора строк может содержать нулевое количество объектов строк. Поскольку Zend_Db_Table_Rowset_Abstract реализует инерфейс Countable, то вы можете использовать count() для определения количества строк в наборе строк.

Пример 9.83. Подсчет количества строк в наборе строк

<?php

$rowset   = $bugs->fetchAll("bug_status = 'FIXED'");

$rowCount = count($rowset);

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

?>

Пример 9.84. Чтение одной строки из набора строк

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

<?php

$bugs   = new Bugs();
$rowset = $bugs->fetchAll("bug_id = 1");
$row    = $rowset->current();

?>

Если набор строк содержит нулевое количество строк, то current() вернет значение null.

Пример 9.85. >Итерация по набору строк

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

<?php

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

?>

После того, как получен отдельный объект строки, вы можете работать с ним, используя методы, описанные в Раздел 9.6, «Zend_Db_Table_Row»

9.7.4. Получение набора строк в виде массива

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

Пример 9.86. Использование toArray()

<?php

$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";
}

?>

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

9.7.5. Сериализация и десериализация наборов строк

Объекты типа Zend_Db_Table_Rowset_Abstract доступны для сериализации. Сериализация производится так же, как и для отдельных объектов строк - вы можете сериализовать объект набора строк и восстановить его позднее.

Пример 9.87. Сериализация набора строк

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

<?php

$bugs   = new Bugs();
$rowset = $bugs->fetchAll();

// Convert object to serialized form
// Преобразование объекта в сериализованную форму
$serializedRowset = serialize($rowset);

// Now you can write $serializedRowset to a file, etc.

?>

Пример 9.88. Десериализация набора строк

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

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

<?php

$rowsetDisconnected = unserialize($serializedRowset);

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

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

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

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

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

<?php

$rowset = unserialize($serializedRowset);

$bugs = new Bugs();

// Переустановка таблицы для набора строк,
// заодно восстанавливается соединение с БД 
$rowset->setTable($bugs);

$row = $rowset->current();

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

?>

Восстановление соединения для набора строк через метод setTable() делает то же самое для всех объектов строк, содержащихся в этом наборе строк.

Это действует только для одного объекта набора строк, но не для других наборов строк или объектов строк, содержащихся в них, даже если эти объекты связаны с одними и теми же строками в БД.

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

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

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

<?php

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

?>

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

Пример 9.91. Пример класса набора строк с новым методом

<?php

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

?>