Класс Zend_Db_Table является объектно-ориентированным интерфейсом к таблицам баз данных. Он предоставляет методы для множества общих операций над таблицами. Базовый класс является расширяемым, поэтому вы можете добавлять свою логику.
Решением Zend_Db_Table является реализация паттерна Table Data Gateway. Это решение также включает в себя класс, реализующий паттерн Row Data Gateway.
Для каждой таблицы в БД, к которой нужен доступ, определяется класс, наследующий от Zend_Db_Table_Abstract.
В этом разделе мы будем использовать для примера базу данных, которая служит для отслеживания ошибок в проекте разработки ПО. Следующий псевдокод для определения данных SQL описывает таблицу в этой БД.
CREATE TABLE bugs ( bug_id INT PRIMARY KEY AUTO_INCREMENT, created_on DATETIME, updated_on DATETIME bug_description VARCHAR, bug_status VARCHAR REFERENCES bug_statuses(bug_status) ); CREATE TABLE bug_status ( bug_status VARCHAR PRIMARY KEY ); CREATE TABLE products ( product_id VARCHAR PRIMARY KEY ); CREATE TABLE bugs_products ( bug_id INT REFERENCES bugs(bug_id), product_id VARCHAR REFERENCES products(product_id), PRIMARY KEY(bug_id, product_id) );
Объявляйте таблицу, для которой определен этот класс, используя
защищенную переменную $_name
. Это строка и она
должна содержать имя таблицы в том же виде, что и в БД.
Пример 9.1. Объявление класса таблицы с явным определением имени таблицы
<?php class Bugs extends Zend_Db_Table_Abstract { protected $_name = 'bugs'; } ?>
Если вы не определелили имя таблицы, то по умолчанию оно равно имени класса. Если вы предпочитаете этот путь, то имя класса должно иметь то же написание, что и имя таблицы в БД.
Пример 9.2. Объявление класса таблицы с неявным определением имени таблицы
<?php class bugs extends Zend_Db_Table_Abstract { // имя таблицы соответствует имени класса } ?>
Вы можете также объявить схему таблицы в защищенной
переменной $_schema
. В некоторых СУРБД вместо
'схема' (schema) используются 'база данных' (database)
или 'пространство таблиц' (tablespace), но они используются
примерно одинаково. Вы можете также объявить схему как
часть имени таблицы.
Пример 9.3. Объявление класса таблицы со схемой
<?php // Первый способ: class Bugs extends Zend_Db_Table_Abstract { protected $_schema = 'bug_db'; protected $_name = 'bugs'; } // Второй способ: class Bugs extends Zend_Db_Table_Abstract { protected $_name = 'bug_db.bugs'; } ?>
Если вы не определили имя схемы, то по умолчанию оно равно схеме, по которой подключен ваш экземпляр адаптера БД.
Каждая таблица должна иметь первичный ключ. Вы можете объявить
столбец для первичного ключа, используя защищенную переменную
$_primary
. Это может быть строка с именем одного
столбца или массив имен столбцов, если первичный ключ является
составным.
Пример 9.4. Пример определения первичного ключа
<?php class Bugs extends Zend_Db_Table_Abstract { protected $_name = 'bugs'; protected $_primary = 'bug_id'; } ?>
Если вы не задали первичный ключ, то Zend_Db_Table_Abstract
пытается определить его, основываясь на данных, получаемых
посредством метода describeTable()
.
Когда создается экземпляр класса таблицы, конструктор вызывает ряд защищенных методов, инициализирующих метаданные для таблицы. Вы можете расширить любые из этих методов для того, чтобы явно определить эти метаданные. Помните, что в конце должен вызываться родительский метод.
Пример 9.5. Пример переопределения метода _setupTableName()
<?php class Bugs extends Zend_Db_Table_Abstract { protected function _setupTableName() { $this->_name = 'bugs'; parent::_setupTableName(); } } ?>
Методы установки могут быть переопределены следующим образом:
_setupDatabaseAdapter()
проверяет, был ли
предоставлен адаптер БД, и извлекает используемый по
умолчанию адаптер из реестра, если необходимо.
Посредством переопределения этого метода вы можете
установить адаптер БД из других источников.
_setupTableName()
по умолчанию получает имя
таблицы из имени класса. Посредством переопределения
этого метода вы можете установить имя таблицы до того,
как будут произведены эти действия по умолчанию.
_setupMetadata()
устанавливает схему, если
имя таблицы содержит структуру "schema.table", вызывает
describeTable()
для получения метаданных,
по умолчанию определяет массив столбцов
$_cols
, полученых
через describeTable()
. Переопределелив этот
метод, вы можете устанавливать столбцы самостоятельно.
_setupPrimaryKey()
по умолчанию
устанавливает столбцы первичного ключа, полученные через
describeTable()
, и проверяет, входят ли
столбцы первичного ключа в массив $_cols
.
Переопределив этот метод, вы можете устанавливать
столбцы первичного ключа.
До того, как начать использование класса таблицы, создайте его экземпляр, используя конструктор. Единственным аргументом конструктора является массив опций. Наиболее важной опцией для конструктора является экземпляр адаптера БД, представляющий текущее соединение к какой-либо СУРБД. Есть три способа передачи адаптера БД классу таблицы, и эти три способа описаны ниже:
Первым способом предоставления адаптера БД классу таблицы
является передача объекта типа Zend_Db_Adapter_Abstract в
массиве опций под ключом 'db'
.
Вторым способом предоставления адаптера БД классу таблицы
является декларирование объекта типа Zend_Db_Adapter_Abstract
как используемого по умолчанию для всех экземпляров таблиц в
вашем приложении. Вы можете делать это через статический метод
Zend_Db_Table_Abstract::setDefaultAdapter()
. Его
аргументом является объект типа Zend_Db_Adapter_Abstract.
Пример 9.7. Пример создания экземпляра таблицы с адаптером, используемым по умолчанию
<?php $db = Zend_Db::factory('PDO_MYSQL', $options); Zend_Db_Table_Abstract::setDefaultAdapter($db); // Далее... $table = new Bugs(); ?>
Может быть удобным создавать объект адаптера БД в центральной части вашего приложения - например, в загрузочном коде - и затем сохранять его как адаптер, используемый по умолчанию. Это дает возможность быть уверенным в том, что во всем приложении используется один и тот же экземпляр адаптера. Но установка адаптера, используемого по умолчанию, ограничена одним экземпляром адаптера.
Третий способ предоставления адаптера БД классу таблицы -
передача строки в массиве опций под ключом 'db'
.
Эта строка используется как ключ для статического экземпляра
Zend_Registry, в котором под этим ключом должен храниться объект
типа Zend_Db_Adapter_Abstract.
Пример 9.8. Пример создания экземпляра таблицы с использованием ключа реестра
<?php $db = Zend_Db::factory('PDO_MYSQL', $options); Zend_Registry::set('my_db', $db); // Далее... $table = new Bugs(array('db' => 'my_db')); ?>
Как и в случае установки адаптера, используемого по умолчанию, это дает возможность быть уверенным в том, что во всем приложении используется один и тот же адаптер, но использование реестра является более гибким, потому что вы можете хранить в нем более одного экземпляра адаптера. Экземпляр адаптера является индивидуальным для конкретной СУРБД и экземпляра БД. Если в вашем приложении необходимо подключаться к нескольким БД или даже различным СУРБД, то вам нужно использовать несколько адаптеров.
Вы можете использовать объект таблицы для добавления строк в таблицу
БД, на которой основан объект таблицы. Для этого используйте метод
insert()
в объекте таблицы. Аргументом является
ассоциативный массив, содержащий имена столбцов и соответствующие им
значения.
Пример 9.9. Пример добавления строк в таблицу
<?php $table = new Bugs(); $data = array( 'created_on' => '2007-03-22', 'bug_description' => 'Something wrong', 'bug_status' => 'NEW' ); $table->insert($data); ?>
По умолчанию значения в вашем массиве данных добавляются как буквенные значения, с использованием параметров. Если вам нужно, чтобы они интерпретировались как выражения SQL, то необходимо обозначить их отличие от простых строк. Для этого используйте объекты типа Zend_Db_Expr.
Пример 9.10. Пример добавления выражений в таблицу
<?php $table = new Bugs(); $data = array( 'created_on' => new Zend_Db_Expr('CURDATE()'), 'bug_description' => 'Something wrong', 'bug_status' => 'NEW' ); ?>
В примере добавления строки выше предполагается, что таблица имеет автоинкрементный первичный ключ. Это принятое по умолчанию поведение Zend_Db_Table_Abstract, но есть и другие типы первичных ключей. Следующий раздел описывает, как поддерживаются различные типы первичных ключей.
Автоинкрементный первичный ключ генерирует уникальное
целочисленное значение, если вы пропустите столбец для
первичного ключа в вашем SQL-операторе INSERT
.
В Zend_Db_Table_Abstract, если защищенная переменная
$_sequence
имеет булево значение true
,
то класс считает, что таблица имеет автоинкрементный столбец.
![]() |
Замечание |
---|---|
Поддержка последовательностей еще не реализована в Zend_Db_Table. Приведенное ниже не работает в текущей версии, но планируется в Zend Framework 1.0. |
Последовательность (sequence) является объектом базы данных, который генерирует уникальные значения, которые могут использоваться как значения уникальных ключей в одной и более таблицах БД.
Если вы присвоили $_sequence
строковое значение,
то Zend_Db_Table_Abstract считает строку именем объекта
последовательности в БД. Последовательность запускается для
генерации нового значения и это значение используется в операции
INSERT
.
![]() |
Замечание |
---|---|
Поддержка естетственных ключей еще не реализована в Zend_Db_Table. Приведенное ниже не работает в текущей версии, но планируется в Zend Framework 1.0. |
Некоторые таблицы имеют естетственные ключи. Это означает, что ключ не генерируется автоматически таблицей или последовательностью. В этом случае вы должны установить значение первичного ключа.
Если вы присвоили $_sequence
булево значение
false
, то Zend_Db_Table_Abstract считает, что
таблица имеет естетственный первичный ключ. Вы должны
предоставлять значения для столбцов первичного ключа в массиве
данных для метода insert()
, иначе метод бросает
исключение Zend_Db_Table_Exception.
Пример 9.13. Пример объявления таблицы с естетственным ключом
<?php class BugStatus extends Zend_Db_Table_Abstract { protected $_name = 'bug_status'; protected $_sequence = false; } ?>
![]() |
Замечание |
---|---|
Все СУРБД поддерживают таблицы с естетственными ключами. Примеры таблиц, часто объявляемых как имеющие естетственные ключи: справочные таблицы, таблицы пересечений в отношениях "многие-ко-многим", многие таблицы с составными ключами. |
Вы можете обновлять строки в таблице БД, используя метод
update
класса таблицы. Этот метод принимает два
аргумента: первый является ассоциативный массив столбцов, которые
требуется изменить, и новых значений, присваиваемых этим столбцам;
вторым - выражение SQL, которое используется в предложении
WHERE
, как условие, по которому отбираются строки,
требущие изменения в операции UPDATE
.
Пример 9.14. Пример обновления строк в таблице
<?php $table = new Bugs(); $data = array( 'updated_on' => '2007-03-23', 'bug_status' => 'FIXED' ); $where = $table->getAdapter()->quoteInto('bug_id = ?', 1234); $table->update($data, $where); ?>
Второй аргумент может быть массивом SQL-выражений. Выражения
объединяются как булевы условия через оператор AND
.
![]() |
Замечание |
---|---|
Значения и идентификаторы в SQL-выражении не заключаются в
кавычки за вас. Если имеются значения или идентификаторы,
которые требуют заключения в кавычки, то вы должны выполнить
это сами. Используйте методы |
Вы можете удалять строки из таблицы базы данных, используя метод
delete()
. Этот метод принимает один аргумент, который
является SQL-выражением, который используется в предложении как
условие, по которому отбираются строки для удаления.
Пример 9.15. Пример удаления строк из таблицы
<?php $table = new Bugs(); $where = $table->getAdapter()->quoteInto('bug_id = ?', 1235); $table->delete($where); ?>
Аргумент может быть массивом SQL-выражений. Выражения объединяются
как булевы условия через оператор AND
.
![]() |
Замечание |
---|---|
Значения и идентификаторы в SQL-выражении не заключаются в
кавычки за вас. Если имеются значения или идентификаторы,
которые требуют заключения в кавычки, то вы должны выполнить
это сами. Используйте методы |
Вы можете запрашивать у таблицы БД строки, соответствующие
определенным значениям в первичном ключе, используя метод
find()
. Первым аргументом этого метода является
единственное значение или массив значений для сравнения с первичным
ключом таблицы.
Пример 9.16. Пример извлечения строк по значениям первичного ключа
<?php $table = new Bugs(); // Запрашивает единственную строку // Возвращает набор строк (Rowset) $rows = $table->find(1234); // Запрашивает несколько строк // Также возвращает набор строк (Rowset) $rows = $table->find(array(1234, 5678)); ?>
Если вы задали единственное значение, то метод вернет максимум одну строку, потому что первичный ключ не может содержать повторяющиеся значения и должна быть максимум одна строка в таблице БД, соответсвующая заданному вами значению. Если вы задали несколько значений, то метод вернет максимум столько строк, сколько несовпадающих значений вы задали.
Метод find()
может возвращать меньше строк, чем вы
задали значений для первичного ключа, если для некоторых значений
нет соответствующих строк в таблице БД. Метод может даже вернуть
нулевое количество строк. Посколько количество возвращаемых строк
переменно, то метод find()
возвращает объект типа
Zend_Db_Table_Rowset_Abstract.
Если первичный ключ является составным, т.e. он состоит из
нескольких столбцов, то можете определить добавочные столбцы как
дополнительные аргументы в методе find()
. Вы должны
предоставить столько аргументов, сколько столбцов в первичном ключе
таблицы.
Для того, чтобы найти несколько строк с составным первичным ключом, передавайте массив для каждого из аргументов. Все эти массивы должны иметь то же количество элементов.
Пример 9.17. Пример извлечения строк по значениям составного первичного ключа
<?php class BugsProducts extends Zend_Db_Table_Abstract { protected $_name = 'bugs_products'; protected $_primary = array('bug_id', 'product_id'); } $table = new BugsProducts(); // Запрашивает единственную строку через составной первичный ключ // Возвращает набор строк (Rowset) $rows = $table->find(1234, 'ABC'); // Запрашивает несколько строк через составной первичный ключ // Также возвращает набор строк (Rowset) $rows = $table->find(array(1234, 5678), array('ABC', 'DEF')); ?>
Вы можете запрашивать набор строк, используя любые условия, отличные
от значений первичного ключа, используя метод
fetchAll()
класса таблицы. Этот метод возвращает объект
типа Zend_Db_Table_Rowset_Abstract.
Пример 9.18. Пример извлечения строк по выражению
<?php $table = new Bugs(); $where = $table->getAdapter()->quoteInto('bug_status = ?', 'NEW'); $rows = $table->fetchAll($where); ?>
Первым аргументом этого метода является SQL-выражение, которое затем
используется в предложении WHERE
, так же, как оно
используется в методах update()
и
delete()
, описанных выше.
![]() |
Замечание |
---|---|
Значения и идентификаторы в SQL-выражении не заключаются в
кавычки за вас. Если имеются значения или идентификаторы,
которые требуют заключения в кавычки, то вы должны выполнить это
сами. Используйте методы |
Вторым аргументом является выражение или массив выражений,
используемые как условие сортировки в предложении
ORDER BY
.
Третьим и четвертым аргументами являются целочисленные значения
количества и смещения, используемые для того, чтобы запрос возвращал
определенное подмножество строк. Эти значения используются в
предложении LIMIT
или эквивалентной логике для тех
СУРБД, которые не поддерживают синтаксис LIMIT
.
Пример 9.19. Пример поиска строк по выражению
<?php $table = new Bugs(); $where = $table->getAdapter()->quoteInto('bug_status = ?', 'NEW'); $order = 'bug_id'; // Вернуть строки начиная с 21-ой и кончая 30-й $count = 10; $offset = 20; $rows = $table->fetchAll($where, $order, $count, $offset); ?>
Все эти аргументы являются опциональными. Если вы их опустите, то возвращаемый результат будет включать в себя все строки таблицы в непредсказуемом порядке.
Вы можете запрашивать единственную строку, используя любое условие,
отличное от значений перичного ключа, используя метод
fetchRow()
класса таблицы. Использование этого метода
аналогично методу fetchAll()
в том, что его аргументы
включают в себя выражение WHERE
и условия сортировки.
Пример 9.20. Пример поиска единственной строки по выражению
<?php $table = new Bugs(); $where = $table->getAdapter()->quoteInto('bug_status = ?', 'NEW'); $order = 'bug_id'; $row = $table->fetchRow($where, $order); ?>
Этот метод возвращает объект типа Zend_Db_Table_Row_Abstract. Если
по заданному вами условию поиска не найдено ни одной строки в
таблице БД, то fetchRow()
вернет значение
null
.
Класс Zend_Db_Table_Abstract предоставляет некоторую информацию о
его метаданных. Метод info()
возвращает массив с
данными о таблице, ее столбцах, первичном ключе и другие метаданные.
Пример 9.21. Пример получения имени таблицы
<?php $table = new Bugs(); $info = $table->info(); echo "The table name is " . $info['name'] . "\n"; ?>
Ключи массива, возвращаемого методом info()
, описаны
ниже.
name => имя таблицы
cols => массив имен столбцов в таблице
primary => массив имен столбцов в первичном ключе
metadata =>
ассоциативный массив, включающий в себя имена столбцов и
соответствующие им данные о столбцах. Это информация,
возвращаемая методом describeTable()
.
rowClass => имя определенного класса, используемого для объектов Row, возвращаемых методами данного экземпляра таблицы. По умолчанию это Zend_Db_Table_Row.
rowsetClass => имя определенного класса, используемого для объектов Rowset, возвращаемых методами данного экземпляра таблицы. По умолчанию это Zend_Db_Table_Rowset.
referenceMap => ассоциативный массив с данными о ссылках на другие таблицы. См. Раздел 9.7.2, «Определение связей».
dependentTables => массив имен классов таблиц, на которые ссылается данная таблица. См. Раздел 9.7.2, «Определение связей».
schema => имя схемы (базы данных, пространства таблиц) для данной таблицы.
По умолчанию методы класса таблицы возвращают наборы строк в экземплярах класса Zend_Db_Table_Rowset и эти наборы строк содержат в себе колекцию экземпляров класса Zend_Db_Table_Row. Вы можете указать альтернативные классы для использования вместо них, но они должны наследовать от классов Zend_Db_Table_Rowset_Abstract и Zend_Db_Table_Row_Abstract соответственно.
Вы можете определить классы строки и набора строк, используя
массив опций конструктора таблицы, под ключами
'rowClass'
и 'rowsetClass'
соответственно. Задавайте имена классов в виде обычных строк.
Пример 9.22. Пример определения классов строки и набора строк
<?php class My_Row extends Zend_Db_Table_Row_Abstract { ... } class My_Rowset extends Zend_Db_Table_Rowset_Abstract { ... } $table = new Bugs( array( 'rowClass' => 'My_Row', 'rowsetClass' => 'My_Rowset' ) ); $where = $table->getAdapter()->quoteInto('bug_status = ?', 'NEW') // Возвращает объект типа My_Rowset, // содержащий массив объектов типа My_Row $rows = $table->fetchAll($where); ?>
Вы можете сменить используемые классы, определив их через методы
setRowClass()
и setRowsetClass()
.
Это применяется к создаваемым впоследствии строкам и наборам
строк и не влияет на классы объектов строк и наборов строк,
созданных ранее.
Пример 9.23. Пример смены используемых для строк и наборов строк классов
<?php $table = new Bugs(); $where = $table->getAdapter()->quoteInto('bug_status = ?', 'NEW') // Возвращает объект типа Zend_Db_Table_Rowset, // содержащий массив объектов типа Zend_Db_Table_Row. $rowsStandard = $table->fetchAll($where); $table->setRowClass('My_Row'); $table->setRowsetClass('My_Rowset'); // Возвращает объект типа My_Rowset, // содержащий массив объектов типа My_Row. $rowsCustom = $table->fetchAll($where); // Объект $rowsStandard по прежнему существует и не изменился ?>
За более подробной информацией о классах строки и набора строк см. Раздел 9.5, «Zend_Db_Table_Row» и Раздел 9.6, «Zend_Db_Table_Rowset».
Вы можете переопределить методы insert()
в своем
классе таблицы. Это дает возможность реализовать собственный
код, который исполняется до того, как будет выполнена операция с
БД. Всегда вызывайте метод родителького класса после своих
действий.
Пример 9.24. Собственная логика для управления отметками времени
<?php class Bugs extends Zend_Db_Table_Abstract { protected $_name = 'bugs'; public function insert($data) { // добавление timestamp if (empty($data['created_on'])) { $data['created_on'] = time(); } return parent::insert($data); } public function update($data, $where) { // добавление timestamp if (empty($data['updated_on'])) { $data['updated_on'] = time(); } return parent::update($data, $where); } } ?>
Вы можете реализовать собственные методы запросов в своем классе
таблицы, если приходится часто делать запросы к таблице с одними
и теми же условиями. Большинство запросов могут быть написаны с
использованием fetchAll()
, но это требует написания
повторяющегося кода для формирования условий запроса, если вам
нужно выполнять запрос в нескольких местах вашего приложения.
Позтому будет удобным добавить метод в класс таблицы для
выполнения часто используемых запросов к таблице.