Построение условий
Qb строит параметризованные фрагменты WHERE, которые можно композировать,
вкладывать и переиспользовать. Каждое значение становится именованной привязкой,
поэтому значения защищены от инъекций по построению. Это руководство охватывает
повседневные шаблоны; полный список операторов — в
справочнике операторов Qb.
Имена колонок вставляются как есть
Привязываются только значения. Аргумент имени колонки подставляется в SQL
дословно, без кавычек и экранирования. Никогда не передавайте пользовательский
ввод как имя колонки — Qb::eq('status', $userInput) безопасно, а
Qb::eq($userInput, 'active') — вектор SQL-инъекции.
Композиция через and / or
Начните с листовых условий и объединяйте их:
use Flytachi\Winter\Cdo\Qb;
$where = Qb::and(
Qb::eq('status', 'active'),
Qb::gte('age', 18),
Qb::isNull('banned_at'),
);
// status = :iqb0 AND age >= :iqb1 AND banned_at IS NULLand / or / xor принимают любое число условий и молча пропускают null или
пустые — именно это делает необязательные фильтры чистыми.
Необязательные (динамические) фильтры
Поскольку условия null выбрасываются, вы можете встраивать их через тернарный
оператор. Пустые списки для in() нужно защищать, так как in() бросает
исключение на пустом массиве.
$where = Qb::and(
Qb::eq('status', 'active'),
$minAge !== null ? Qb::gte('age', $minAge) : null,
$country !== null ? Qb::eq('country', $country) : null,
$tagIds ? Qb::in('tag_id', $tagIds) : null, // защита пустого массива
);Защищайтесь от полностью пустого условия
Если все необязательные фильтры пропущены, Qb::and(...) даёт пустой
фрагмент. Передача его в update() или delete() порождает запрос без WHERE.
Проверяйте $where->getQuery() !== '' перед запуском мутации.
Управление приоритетом через clip
SQL связывает AND крепче, чем OR. Когда вы их смешиваете, оберните группу
OR в clip(), чтобы навязать нужную группировку:
// Неверно: читается как (published AND role='editor') OR role='admin'
Qb::and(
Qb::eq('published', true),
Qb::or(Qb::eq('role', 'editor'), Qb::eq('role', 'admin')),
);
// Верно: clip делает OR единым сгруппированным членом
Qb::and(
Qb::eq('published', true),
Qb::clip(
Qb::or(Qb::eq('role', 'editor'), Qb::eq('role', 'admin')),
),
);
// published IS TRUE AND (role = :iqb0 OR role = :iqb1)Пошаговое построение (изменяемое)
Qb неизменяем по умолчанию, но методы addAnd / addOr / addXor мутируют на
месте — удобно, когда вы накапливаете условия в цикле:
$where = Qb::eq('status', 'active');
foreach ($filters as $column => $value) {
$where->addAnd(Qb::eq($column, $value));
}Переиспользование значения через именованную привязку
По умолчанию каждое значение получает непрозрачный плейсхолдер (:iqb0). Чтобы
сослаться на одно и то же значение в нескольких условиях, постройте CDOBind
один раз и передайте его — привяжется единственный :name, общий для каждого
использующего его условия:
use Flytachi\Winter\Cdo\CDOBind;
$uid = new CDOBind('uid', $currentUserId);
$where = Qb::or(
Qb::eq('author_id', $uid),
Qb::eq('reviewer_id', $uid),
Qb::eq('assignee_id', $uid),
);
// author_id = :uid OR reviewer_id = :uid OR assignee_id = :uidАварийный выход: raw
Когда ни один оператор не подходит (функции вендора, подзапросы), Qb::raw()
вставляет SQL дословно — но по-прежнему позволяет привязывать значения через
плейсхолдеры:
Qb::raw('JSON_CONTAINS(tags, :tag)', ['tag' => '"php"']);
// JSON_CONTAINS(tags, :tag) — :tag привязан, текст функции — дословныйraw не санитизирует строку запроса
Строка SQL в raw() выдаётся ровно как написана. Никогда не подставляйте в неё
пользовательский ввод — передавайте значения через аргумент $binds.
Использование фрагмента
update() и delete() потребляют Qb напрямую. Для чтения разберите его на
части и привяжите к PDO-запросу:
$stmt = $cdo->prepare("SELECT * FROM users WHERE " . $where->getQuery());
foreach ($where->getBinds() as $bind) {
$stmt->bindValue($bind->getName(), $bind->getValue());
}
$stmt->execute();Связанное
- Операторы Qb — каждый оператор и его SQL
- Обновление и удаление — условия как
WHERE - Конфигурация — детали
CDOBind - Модель работы — почему фрагменты несут свои привязки