Класс Quoter предназначен для безопасного конструирования SQL-запросов из шаблонов с использованием типизированных меток-плейсхолдеров (placeholders), далее — просто «меток».
Класс Db является наследником стандартного класса PDO и расширяет его несколькими сервисными функциями. В этих функциях используются шаблоны с типизированными метками.
Метка — это место в шаблоне SQL-запроса, на место которой будут вставлены реальные данные. Например, в шаблоне select * from users where id = ? or login = ? символы ? является метками. Реальный SQL-запрос из этого шаблона будет конструироваться, например, так $sql = $quoter->format("select * from users where id = ? or login = ?", array(42, "admin"));. Результатом этого вызова будет строка select * from users where id = 42 or login='admin'.
SQL-шаблоны с метками нужны для того, чтобы правильно форматировать данные перед вставкой в SQL-запрос. Например, в примере выше число (42) вставляется в запрос без дополнительной обработки, а строка ("admin") заключается в одинарные кавычки. Для других типов данных есть другие правила преобразования. Часто определение типа производится автоматически, на основании типа входных данных. Это определение не всегда происходит правильно (например, из GET- и POST-параметров всегда приходят только строки). Поскольку SQL — типизированный язык, важно соблидать тип данных и не сравнивать, например, строку с логической величиной. Для этого и придуманы типизированные метки, которые в самом шаблоне указывают, каким должен быть тип данных в это месте.
С типизированными метками шаблон, приведённый выше, выглядит так: select * from users where id = ?i or login = ?s. Метка ?i означает, что в шаблон надо вставить целое число, метка ?s означает строку. Теперь можно написать так: $sql = $quoter->format("select * from users where id = ? or login = ?", array("42", "admin")); и всё равно получить корректный результат.
Подробно об идее и теории типизированных меток можно прочесть в статьях http://habrahabr.ru/post/148701/ и http://dklab.ru/lib/DbSimple/manual.html.
Все метки в данной библиотеке основаны на символе ?, после которого следует тип метки. Если нужно просто вставить символ ? в запрос, без интерпретации его как метки, то используйте удвоение: ??.
Скалярные метки предназначены для вставки в запрос скалярных величин — строк, чисел и т. д.
? — автотип — форматирование осуществляется на основании типа входных данных;
?s — строка;
?i — целое число;
?f — дробное число;
?b — логическое значение;
?t — строка-идентифик��тор объекта базы данных (имя таблицы, колонки и т. п.);
?! — raw-метка — значение вставляется в запрос как есть, без форматирования. Полезно для вставки уже отформатированных SQL-фрагментов.
Пример шаблона, использующего скалярные метки: select * from ?t where id = ?i and ?b.
Если в качестве данных передано некорректное для данного типа значение (например, нечисловая строка передана в метку с типом ?i), то используется «нулевое» значение данного типа (пустая строка, ноль, false). Если в шаблон передан null, он вставляется в результат как SQL-значение NULL или UNKNOWN.
Исключением являются типы ?t и ?! — первый требует на входе строго непустые строки, в противном случае выбрасывается исключение, а второй любое значение приводит к строке и вставляет без изменения.
У значения NULL (UNKNOWN) есть особенность — в SQL его нельзя сравнивать при помощи оператора = или <>. Поэтому при вставке NULL-а проверяется предшествующий ему оператор, и = заменяется на IS, а <> — на IS NOT. Таким образом, a = ? заменяется на a IS NULL.
Метки-массивы нужны для вставки в запросы сложных структур — массивов и хэшей.
?@[тип] — типизированный массив для операторов IN / NOT IN. Массив предполагается однородным — заполненным величинами одного типа, этот тип указывается после символа @. Если тип не указан, то он выводится из типа входных данных. Пример: ?@i — метка для массива целых чисел.
Если переданный массив не пуст, то в запрос вставляются значения из массива, разделённые запятыми. Если массив пуст или передан не массив, то вставляется NULL.
Пример: $quoter->format("… where id in (?@i)", array(array(1, 2, 3))); вернёт … where id in (1, 2, 3).
?#v — хэш значений для оператора INSERT. Тип ключей всегда ?t, тип значений выводится автоматически. Если передан хэш array(k1 => v1, k2 => v2, …), то в запрос вставится (k1, k2, …) values (v1, v2, …). Если хэш пуст, вставляется строка default values, если значение некорректно, то выбрасывется исключение.
?#u — хэш значений для оператора UPDATE. Тип ключей всегда ?t, тип значений выводится автоматически. Если передан хэш array(k1 => v1, k2 => v2, …), то в запрос вставится k1=v1, k2=v2, …. Если хэш пуст или значение некорректно, то выбрасывется исключение.
Примеры: insert into users ?#v, update users set ?#u where id = ?i.
SQL-запрос формируется из строки-шаблона и массива параметров, значения которых вставляются в места меток шаблона. Массив параметров может быть а��социативным, тогда для работы с ним следует использовать именованные метки. Имя метки совпадает с ключом в массиве параметров и записывается перед символом метки (?) без пробела. Допустимые имена меток имеют формат [a-zA-Z_][a-zA-Z0-9_]*. Пример:
$quoter->format("select * from users where id = userId?i", array("userId" => 42));Если массив параметров не ассоциативный, то вместо имени метки можно использовать порядковый номер параметра в массиве (нумерация с единицы). Например:
$quoter->format("select * from users where id = 1?i", array(42));Это может быть удобным, если один параметр используется в запросе несколько раз в разных местах. В этом случае можно не дублировать его в массиве параметров, а просто сослаться из шаблона на нужную позицию в массиве.
Пустое имя/номер метки обрабатывается как автоматически увеличивающийся номер параметра с числовым ключом. То есть, первая (по ходу шаблона) метка с пустым именем соответствует параметру на позиции 1, вторая — 2 и т. д.
Если в параметрах нет какого-либо из требуемых ключей или есть лишние, то выбрасывается исключение.
\Yaff\db\Quoter — абстрактный класс, имеющий (пока что) две реализации: QuoterSQL — минимальный стандартный SQL-синтаксис и QuoterPostgres — синтаксис PostgreSQL.
Основным методом Quoter-а является метод format($query, array $args = array()). Аргумент $query — это шаблон запроса с типизированными метками, а $args — массив аргументов, которые будут подставлены на место меток. Метод возвращает собранную из шаблона и аргументов SQL-строку.
$quoter = new \Yaff\db\QuoterSQL();
$sql = $quoter->format("select * from users where id = ?i", array(42));Результатом будет строка select * from users where id = 42.
Класс \Yaff\db\Db предназначен для упрощения наиболее частых операций с базой данных. Он является наследником библиотечного класса PDO и использует внутри себя Quoter для подстановки данных в шаблоны. Класс предлагает следующие дополнительные сервисные методы (почти все они принимают SQL-шаблон $sqlTpl и массив аргументов $args):
perform($sqlTpl, array $args = array()) — выполнить запрос, не возвращая результат;
performInsert($tableName, array $values = array()) — выполнить INSERT-запрос в таблицу $tableName;
getRow($sqlTpl, array $args = array()) — вернуть первую строку результата запроса как ассоциативный массив;
getAll($sqlTpl, array $args = array()) — вернуть все строки результата запроса как массив ассоциативных массивов;
getOne($sqlTpl, array $args = array()) — вернуть значение первой колонки первой строки результата запроса;
getCol($sqlTpl, array $args = array(), $n = 0) — вернуть $n-ю колонку из результатов запроса в виде массива;
getAssoc($sqlTpl, array $args = array(), $forceArray = false) — вернуть результаты запроса в виде ассоциативного массива, в котором первая колонка каждой строки является ключом, а последующие — значением (массивом). Если в результате всего две колонки, то значение не преобразуется в массив, это можно изменить, выставив последний параметр метода в TRUE.