5.3. Zend_Db_Select

5.3.1. Введение

Zend_Db_Select является инструментом, помогающим строить SQL-операторы SELECT, не зависящие от типа СУБД. Очевидно, он не может быть идеальным, но он проделал значительный путь к тому, чтобы помочь сделать ваши запросы переносимыми между серверными СУБД. Кроме того, он помогает сделать ваши запросы устойчивыми против SQL-инъекций.

Наиболее легкий способ создания экземпляра Zend_Db_Select -- использование метода Zend_Db_Adapter::select().

<?php
		
require_once 'Zend/Db.php';

$params = array (
    'host'     => '127.0.0.1',
    'username' => 'malory',
    'password' => '******',
    'dbname'   => 'camelot'
);

$db = Zend_Db::factory('PDO_MYSQL', $params);

$select = $db->select();
// $select теперь является объектом, сконфигурированным для использования
// исключительно с адаптером PDO_MYSQL 

?>

Далее вы строите запрос SELECT, используя этот объект и его методы, затем генерируете строку, которая передается Zend_Db_Adapter для запросов (метод query()) или извлечения (группа методов fetch*()).

<?php
	
//
// SELECT *
//     FROM round_table
//     WHERE noble_title = "Sir"
//     ORDER BY first_name
//     LIMIT 10 OFFSET 20
//

// вы можете делать это последовательно...
$select->from('round_table', '*');
$select->where('noble_title = ?', 'Sir');
$select->order('first_name');
$select->limit(10,20);

// ...или в "потоковом" стиле (fluent style):
$select->from('round_table', '*')
       ->where('noble_title = ?', 'Sir')
       ->order('first_name')
       ->limit(10,20);

// извлечение результата
$sql = $select->__toString();
$result = $db->fetchAll($sql);

// также вы можете передавать сам объект $select
// Zend_Db_Adapter достаточно "разумен", чтобы вызывать метод __toString()
// для объектов Zend_Db_Select, чтобы получить из них строку запроса
$result = $db->fetchAll($select);

?>

Еще вы можете применять связанные параметры в своих запросах вместо подстановки по порядку следования.

<?php
	
//
// SELECT *
//     FROM round_table
//     WHERE noble_title = "Sir"
//     ORDER BY first_name
//     LIMIT 10 OFFSET 20
//

$select->from('round_table', '*')
       ->where('noble_title = :title')
       ->order('first_name')
       ->limit(10,20);

// извлечение результатов с использованием связанных параметров
$params = array('title' => 'Sir');
$result = $db->fetchAll($select, $params);

?>

5.3.2. Извлечение столбцов

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

<?php
	
// создание объекта $db, затем получение инструмента SELECT
$select = $db->select();

// SELECT a, b, c FROM some_table
$select->from('some_table', 'a, b, c');
// эквивалент:
$select->from('some_table', array('a', 'b', 'c');

// SELECT bar.col FROM foo AS bar
$select->from('foo AS bar', 'bar.col');

// SELECT foo.col AS col1, bar.col AS col2 FROM foo, bar
$select->from('foo', 'foo.col AS col1');
$select->from('bar', 'bar.col AS col2');

?>

5.3.3. Объединение таблиц

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

<?php
	
// создание объекта $db, затем получение инструмента SELECT
$select = $db->select();

//
// SELECT foo.*, bar.*
//     FROM foo
//     JOIN bar ON foo.id = bar.id
//
$select->from('foo', '*');
$select->join('bar', 'foo.id = bar.id', '*');

?>

В настоящее время поддерживается только конструкция JOIN. Конструкции LEFT JOIN, RIGHT JOIN и т.д. не поддерживаются. Будущие релизы будут поддерживать их в форме, независимой от типа выбранной СУБД.

5.3.4. Условия WHERE

Для добавления условий WHERE, используйте метод where(). Вы можете передать либо обычную строку, либо строку с метками заполнения и значения для подстановки с добавлением кавычек (подстановка выполняется с помощью Zend_Db_Adapter::quoteInto).

Повторные вызовы where() будет добавлять условия через AND. Если вам нужен OR, используйте метод orWhere().

<?php
	
// создание объекта $db, затем получение  SELECT
$select = $db->select();

//
// SELECT *
//     FROM round_table
//     WHERE noble_title = "Sir"
//     AND favorite_color = "yellow"
//
$select->from('round_table', '*');
$select->where('noble_title = "Sir"'); // встроенное значение
$select->where('favorite_color = ?', 'yellow'); // подставляемое значение

//
// SELECT *
//     FROM foo
//     WHERE bar = "baz"
//     OR id IN("1", "2", "3")
//
$select->from('foo', '*');
$select->where('bar = ?', 'baz');
$select->orWhere('id IN(?)', array(1, 2, 3);

?>

5.3.5. Группировка

Для группировки строк используйте метод group() столько раз, сколько вам нужно.

<?php
	
// создание объекта $db, затем получение инструмента SELECT
$select = $db->select();

//
// SELECT COUNT(id)
//     FROM foo
//     GROUP BY bar, baz
//
$select->from('foo', 'COUNT(id)');
$select->group('bar');
$select->group('baz');

// эквивалентный вызов group():
$select->group('bar, baz');

// другой эквивалентный вызов group():
$select->group(array('bar', 'baz'));

?>

5.3.6. Условия HAVING

Для добавления условий HAVING используйте метод having(). Этот метод по функциям идентичен методу where().

Если вы вызываете having() несколько раз, условия будут добавляться через AND. Если вам нужен OR, используйте метод orHaving().

<?php
	
// создание объекта $db, затем получение инструмента SELECT
$select = $db->select();

//
// SELECT COUNT(id) AS count_id
//     FROM foo
//     GROUP BY bar, baz
//     HAVING count_id > "1"
//
$select->from('foo', 'COUNT(id) AS count_id');
$select->group('bar, baz');
$select->having('count_id > ?', 1);

?>

5.3.7. Сортировка

Для сортировки столбцов используйте метод order() столько раз, сколько нужно.

<?php
	
// создание объекта $db, затем получение инструмента SELECT
$select = $db->select();

//
// SELECT * FROM round_table
//     ORDER BY noble_title DESC, first_name ASC
//
$select->from('round_table', '*');
$select->order('noble_title DESC');
$select->order('first_name');

// эквивалентный вызов order():
$select->order('noble_title DESC, first_name');

// другой эквивалентный вызов order():
$select->order(array('noble_title DESC', 'first_name'));

?>

5.3.8. Ограничение по количеству строк и смещению

Zend_Db_Select предлагает независимую от типа СУБД поддержку LIMIT. Для многих баз данных -- таких, как MySQL и PostgreSQL -- это относительно легко, так как они поддерживают синтаксис "LIMIT :количество_строк [OFFSET :смещение]".

Для других баз данных -- таких, как Microsoft SQL и Oracle -- это не так легко, поскольку они совсем не поддерживают конструкцию LIMIT. MS-SQL имеет только конструкцию TOP, Oracle требует, чтобы запрос был написан специальным образом для эмуляции LIMIT. Благодаря внутреннему алгоритму работы Zend_Db_Select, мы можем "на лету" переписывать SELECT для эмуляции функционала LIMIT из вышеупомянутых СУБД с открытыми исходниками.

Для ограничения возвращаемых результатов по количеству строк и смещению используйте метод limit() c количеством строк и смещением (необязательный параметр).

<?php
	
// простой "LIMIT :количество_строк"
$select = $db->select();
$select->from('foo', '*');
$select->order('id');
$select->limit(10);

//
// в MySQL/PostgreSQL/SQLite это будет преобразовано в:
//
// SELECT * FROM foo
//     ORDER BY id ASC
//     LIMIT 10
//
// а в Microsoft SQL это будет преобразовано в:
//
// SELECT TOP 10 * FROM FOO
//     ORDER BY id ASC
//
//

// а теперь более сложный "LIMIT :количество_строк OFFSET :смещение"
$select = $db->select();
$select->from('foo', '*');
$select->order('id');
$select->limit(10, 20);

//
// в MySQL/PostgreSQL/SQLite это будет преобразовано в:
//
// SELECT * FROM foo
//     ORDER BY id ASC
//     LIMIT 10 OFFSET 20
//
// в Microsoft SQL, который не поддерживает смещения, это будет
// преобразовано в нечто подобное:
//
// SELECT * FROM (
//     SELECT TOP 10 * FROM (
//         SELECT TOP 30 * FROM foo ORDER BY id DESC
//     ) ORDER BY id ASC
// )
//
// Zend_Db_Adapter автоматически сделает перобразования за вас
//

?>

5.3.9. Ограничение по странице и количеству строк

Zend_Db_Select также предлагает ограничение, основанное на страницах. Если вы хотите получить определенную "страницу" результатов, используйте метод limitPage(); сначала передается номер требуемой страницы, затем количество строк, которое показывается на каждой странице.

<?php
	
// построение основы запроса SELECT...
$select = $db->select();
$select->from('foo', '*');
$select->order('id');

// ... и ограничение до страницы 3, где каждая страница имеет 10 строк
$select->limitPage(3, 10);

//
// в MySQL/PostgreSQL/SQLite это будет преобразовано в:
//
// SELECT * FROM foo
//     ORDER BY id ASC
//     LIMIT 10 OFFSET 20
//

?>