WHERE
Where – класс определяет фрагмент WHERE SQL выражения. Данный класс позволяет получить sql любой сложности и массив биндов. Экземпляр класса получаем через фабрику (смотри раздел УСТАНОВКА).
В сгенерированное sql выражение не входит ключевое слово where!
Определимся с понятиями
Приведенные здесь определения не являются научными, это просто термины адаптированные для решения задачи и введенные для удобства.
Логическое выражение – набор логических операций. В нашем случае это where часть sql запроса.
Логическая операция – наименьшая автономная часть where запроса.
Пример: name like '%Вас%'
Под капотом в классе Where логическое выражение представлено в виде массива логических операций, которые в свою очередь также являются массивами.
Логическая операция: [колонка, знакСравнения, значение]
Пример: [‘name’, ‘like’, ‘%Вас%’]
Рассмотрим как же sql выражение curse = 1 and (old < 18 or old > 50)
будет представлено под капотом:
[ ['curse', '=', 1], 'and', [ ['old', '<', 18], 'or', ['old', '>', 50] ] ]
Данную структуру будем называть сырое выражение.
Сырое выражение можно получить с помощью метода getRaw()
и задать методом setRaw($raw)
.
Самим составлять сырое выражение не нужно.
Методы linkAnd() и linkOr()
В простом случае, если в where логические операции связаны только оператором И или только ИЛИ, тогда можно использовать linkAnd()
и linkOr()
соответственно.
// sql: old > 18 and old < 50 $wh-> linkAnd([ ['old', '<', 18], ['old', '>', 50] ]); // sql: old < 18 or old > 50 $wh-> linkOr([ ['old', '<', 18], ['old', '>', 50] ]);
Если же логическое выражение содержит всего одну логическую операцию, то можно использовать любой из двух методов и можно указать только массив логической операции, не обертывая его в еще один массив: $wh->linkOr(['old', '<', 18])
.
Sql любой сложности можно описать тремя методами: linkAnd(), linkOr(), getRaw()
. Рассмотрим на примере уже знакомого sql выражения:
// sql: curse = 1 and (old < 18 or old > 50) $wh->linkAnd([ ['curse', '=', 1], $wh->linkOr([ ['old', '<', 18], ['old', '>', 50] ])->getRaw() ]);
В классе Where есть приватная переменная, в которой хранится сырое выражение. Методы linkAnd()
и linkOr()
перезаписывают эту переменную, поэтому при составлении логического выражения методы вкладываются друг в друга и переменная с сырым выражением содержит данные полученные из последнего выполненного метода.
Логическая операция
Мы рассматриваем логическую операцию, задаваемую с помощью методов linkAnd()
и linkOr()
. Для функции setRaw()
, которая напрямую работает с сырым выражением, написанное здесь работать не будет.
В простом случае логическая операция представляет собой массив: [колонка, знакСравнения, значение]
.
Первый элемент массива - это просто строка, вставляющаяся в sql запрос без изменений, а следовательно в ней можно писать sql функции.
Пример: ['LOWER(name)', 'like', '%вас%']
Второй элемент - это то же строка, вставляющаяся в sql без изменений между двумя операндами. Может принимать следующие значения: =, >, <, >=, <=, <>, like, not like, between, not between, in, not in.
Третий элемент массива может быть как числового, так и строкового типа. Where класс автоматически подставит в sql запрос вместо значения псевдоним. Например name like :al_where_UsK63hsfVW
. Значения можно получить с помощью функции getBind()
.
Элемент массива с ключом sql. Бывает нужно, чтобы значение вставлялось в sql код без изменений. Например, для применения функций. Этого можно добиться, указав в качестве ключа ‘sql’.
Пример: [‘LOWER(name)’, ‘like’, ‘sql’ => "LOWER('$name')"]
Элемент массива с ключом bind в логической операции используется для хранения массива биндов, где ключи – псевдонимы. Приведённый выше пример неправильный с точки зрения безопасности. Нельзя вставлять переменные в sql – черевато инъекциями. Поэтому в данном случае нужно будет самому указывать псевдонимы, например так:
['LOWER(name)', 'like', 'sql' => "LOWER(:name)", 'bind'=> ['name' => $name] ]
Функция flex($column, $value) сама по себе является логической операцией и полезна, если нужен поиск по тексту без учета регистра.
Пример:
// Sql: curse = 1 and LOWER(name) like LOWER(':aliasName') // без функции flex $name = 'м%А'; $wh->linkAnd([ ['curse', '=', 1], [ 'LOWER(name)', 'like', 'sql' => "LOWER(':aliasName')", 'bind'=> ['aliasName' => $name] ] ]); // с функцией flex $name = 'м*А'; $wh->linkAnd([ ['curse', '=', 1], $wh->flex('name', $name) ]);
Обратите внимание, что в обычном случае в качестве маски используется процент (%
), а в функции flex()
используется звездочка (*
). Есть еще fullFlex($column, $value)
, это то же самое, что flex($column, "*$value*")
Оператор in в логической операции можно записать так: ['curse', 'not in', [1, 3, 5]]
. Класс Where преобразует такую запись в следующий sql код: curse not in (:al_where_jCgWfr95kh, :al_where_mCqefr95kh, :al_where_jCfgfr9Gkh)
Оператор between в логической операции можно записать так: ['curse', ' between', 1, 5]
. Класс Where преобразует такую запись в следующий sql код: curse between :al_where_Pi4CRr4xNn and :al_where_WiPPS4NKiG
Будьте внимательны в логической операции с ключевым словом between
,
если третий и четвертый элемент массива это строка, то применяется особая логика. В данном случае считается, что
идет выборка из диапазона дат и, следовательно, применяется sql функция приведения строки к дате
(функция strToDate($aliasDate)
из трейта BaseFunctions
).
Пример:
-
['curse', ' between', 1, 5]
- 3 и 4 элемент числа, преобразования нет
sql:curse between :al_where_Pi4CRr4xNn and :al_where_WiPPS4NKiG
-
['build_date', 'between', '01.01.2016', '01.01.2019']
3 и 4 элемент массива - это строка, следовательно, указана дата, происходит преобразование
sql:build_date between TO_DATE(:al_where_fkD7nZg5lU, 'dd.mm.yyyy hh24:mi::ss') and TO_DATE(:al_where_LdyVRznPF8, 'dd.mm.yyyy hh24:mi::ss')
Описание функций и методов
Метод | Описание |
---|---|
__construct() | Экземпляр класса создается в фабрике и при создании в конструктор передается массив настроек (смотри раздел УСТАНОВКА). |
getRaw() | Получить сырое выражение. |
setRaw($raw) |
$raw - Массив. Сырое выражение, описывающее логическое выражение.
Задать сырое выражение. |
getSql() |
Получить строку sql запроса, данные формируются после вызова функции generate() .
|
getBind() |
Получить массив биндов, где ключи - это псевдонимы (алиасы). Данные формируются после вызова функции generate() .
|
linkAnd($expression) linkOr($expression) |
$expression - массив логических операций.
Формируют из массива логических операций – логическое выражение. Подробнее в подразделах Методы linkAnd() и linkOr() и Логическая операция |
flex($column, $value) |
$column - Колонка в базе данных, по которой осуществляется поиск. $value) - Поисковая фраза. Возвращает логическую операцию адаптированную для поиска в тексте без учета регистра. Подробнее в подразделе Логическая операция. |
notFlex($column, $value) | То же самое, что и flex(), только в сгенерированный sql добавляется предлог not. |
fullFlex($column, $value) |
То же самое, что flex($column, "*$value*") . Подробнее в подразделе Логическая операция.
|
repStar($str) |
$str - Поисковая строка.
Заменяются * на %, причем можно экранировать \*, тогда * не заменяются (для оператора like). |
generate() | Генерирует из сырого выражения строку sql запроса и массив биндов. |
Примеры
Пример 1
Используем максимальное количество элементов класса Where в запросе.
Выборка всех системных блоков с процессором i5 или i7, оперативкой между 4 и 32 гигами. Материнская плата должна быть одной из следующих марок: ASUS, GIGABYTE, Intel, MSI. Дата создания сборки должна быть между 01.01.2016 и 01.01.2019. Средняя оценка сборки не должна быть ниже 3, а автора сборки должны звать Вася.
sql:(core = 'i5' or core = 'i7') and size_ram between 4 and 32 and mark_motherboard in ('ASUS', 'GIGABYTE', 'Intel', 'MSI') and build_date between TO_DATE('01.01.2016', 'dd.mm.yyyy hh24:mi::ss') and TO_DATE('01.01.2019', 'dd.mm.yyyy hh24:mi::ss') and avg(build_rating) >= abs(3) and lower(author_build) like lower('%Вася%')php:
// Создаем экземпляры классов Where с помощью фабрики подробнее в документации в разделе УСТАНОВКА // (здесь случай компонента для yii2) $wh = Yii::$app->gp->where(); $sqlWhere = $wh->linkAnd([ $wh->linkOr([ ['core', '=', 'i5'], ['core', '=', 'i7'] ])->getRaw(), ['size_ram', 'between', 4, 32], ['mark_motherboard', 'in', ['ASUS', 'GIGABYTE', 'Intel', 'MSI'] ], ['build_date', 'between', '01.01.2016', '01.01.2019'], ['avg(build_rating)', '>=', 'sql' => 'abs(:x)', 'bind' => ['x' => 3]], $wh->fullFlex('author_build', 'Вася') ])->generate()->getSql(); $bindWhere = $wh->getBind(); /* Содержимое переменной $sqlWhere: ( core = :al_where_FqzYHPa9TD or core = :al_where_wSqo2YqqS5 ) and size_ram between :al_where_wp5urPaF7F and :al_where_9DWystdstW and mark_motherboard in ( :al_where_WQFd4y2lPL , :al_where_gb1cwjSwOQ , :al_where_1NjObC7fUr , :al_where_2H7xL2WEdB ) and build_date between TO_DATE(:al_where_fkD7nZg5lU, 'dd.mm.yyyy hh24:mi::ss') and TO_DATE(:al_where_LdyVRznPF8, 'dd.mm.yyyy hh24:mi::ss') and avg(build_rating) >= abs(:x) and LOWER(author_build) like LOWER(:al_where_Ha7bUPnrFE) Содержимое переменной $bindWhere: Array ( [al_where_FqzYHPa9TD] => i5 [al_where_wSqo2YqqS5] => i7 [al_where_wp5urPaF7F] => 4 [al_where_9DWystdstW] => 32 [al_where_WQFd4y2lPL] => ASUS [al_where_gb1cwjSwOQ] => GIGABYTE [al_where_1NjObC7fUr] => Intel [al_where_2H7xL2WEdB] => MSI [al_where_fkD7nZg5lU] => 01.01.2016 [al_where_LdyVRznPF8] => 01.01.2019 [x] => 3 [al_where_Ha7bUPnrFE] => %Вася% ) */
Пример 2
Пример со множеством вложенных скобок.
sql:core = 'i7' or ( core = 'i5' and orm = 64 and ( LOWER(mark_monitor) like LOWER('%apple%') or ( LOWER(mark_monitor) like LOWER('%samsung%') and diagonal = 27) ) )php:
$sqlWhere = $wh->linkOr([ ['core', '=', 'i7'], $wh->linkAnd([ ['core', '=', 'i5'], ['orm', '=', '64'], $wh->linkOr([ $wh->fullFlex('mark_monitor', 'apple'), $wh->linkAnd([ $wh->fullFlex('mark_monitor', 'samsung'), ['diagonal', '=', 27] ])->getRaw() ])->getRaw() ])->getRaw() ])->generate()->getSql(); $bindWhere = $wh->getBind(); /* Содержимое переменной $sqlWhere: core = :al_where_9yIsG56PAg or ( core = :al_where_LX8Pu4fIiQ and orm = :al_where_ydlEfqrfz2 and ( LOWER(mark_monitor) like LOWER(:al_where_w1dH6AonLZ) or ( LOWER(mark_monitor) like LOWER(:al_where_4EgcfW9iLu) and diagonal = :al_where_Azr8RX4ODu ) ) ) Содержимое переменной $bindWhere: Array ( [al_where_9yIsG56PAg] => i7 [al_where_LX8Pu4fIiQ] => i5 [al_where_ydlEfqrfz2] => 64 [al_where_w1dH6AonLZ] => %apple% [al_where_4EgcfW9iLu] => %samsung% [al_where_Azr8RX4ODu] => 27 ) */