Пакет · mui-data-grid

Режимы совпадения

Четыре строковых оператора MUI (contains, notContains, startsWith, endsWith) регистронезависимы по определению, а переносимого способа записать это в SQL не существует. TextMatch — перечисление, которое выбирает написание; по умолчанию оно определяется по драйверу вашей базы.

Зачем это нужно

Одна и та же операция пишется в трёх СУБД тремя способами, и разница не косметическая:

СУБД Регистронезависимое совпадение Что будет с ILIKE
PostgreSQL col ILIKE :v работает
MySQL / MariaDB col LIKE :v (при коллации *_ci) синтаксическая ошибка
SQLite col LIKE :v (регистр ASCII) синтаксическая ошибка

То есть на MySQL и SQLite ILIKE — не «медленнее» и не «менее точно», а полностью нерабочий запрос. Поэтому написание нельзя зашить в библиотеку: оно принадлежит соединению, а не гриду.

Значения

Case SQL для contains Для чего
TextMatch::Auto определяется по драйверу репозитория значение по умолчанию
TextMatch::ILike col ILIKE :v PostgreSQL
TextMatch::Like col LIKE :v MySQL / SQLite — регистр решает коллация
TextMatch::Lower lower(col) LIKE lower(:v) любая СУБД, включая свёртку не-ASCII

Остальные девятнадцать операторов — обычный стандартный SQL, режим их не касается.

Автоопределение

В режиме Auto метод MuiGrid::wrap() разрешает написание один раз за вызов, по драйверу базы, с которой работает переданный репозиторий:

Драйвер PDO Режим
pgsql ILike
mysql Like
sqlite Like
любой другой Lower

Драйвер берётся из конфигурации базы, привязанной к репозиторию. Конфигурация к этому моменту уже зарегистрирована пулом соединений и лежит в его кэше, поэтому определение стоит одного обращения к статическому массиву, а не нового соединения. Если конфигурацию разрешить не удалось — нет пула, класс не зарегистрирован, в тестах подставлен двойник — применяется портируемый Lower, а не исключение.

php
$schema = GridSchema::make(
  GridColumn::for('title', 'a.title')->filterable(FilterType::String)->sortable(),
);

// на PostgreSQL → a.title ILIKE :iqb0
// на MySQL и SQLite → a.title LIKE :iqb0
// код при этом один и тот же

Задать вручную

Автоопределение знает драйвер, но не знает вашей коллации. Явный режим нужен в трёх случаях: бинарная коллация MySQL, где LIKE регистрозависим; экзотический драйвер; построение условия без живого пула соединений.

На всю схему

php
GridSchema::make(/* … */)->textMatch(TextMatch::Lower);

На одну колонку

php
GridColumn::for('name', 'au.name')
  ->filterable(FilterType::String)
  ->textMatch(TextMatch::Lower);

Режим колонки перекрывает режим схемы. Значение TextMatch::Auto на колонке означает «наследовать от схемы» — то же самое, что не вызывать метод.

Методы

TextMatch::forDriver()

Возвращает конкретный режим для имени драйвера PDO.

Синтаксис

php
public static function forDriver(string $driver): self

Параметры

$driver — имя драйвера, например pgsql, mysql, sqlite. Регистр не важен.

Возвращает

TextMatch — конкретный режим; для нераспознанного драйвера — Lower.

TextMatch::forRepository()

Возвращает конкретный режим для базы, с которой работает репозиторий.

Полезен за пределами грида: если вы строите собственное условие поиска, тот же вызов сделает его портируемым — см. Фильтры вне грида.

Синтаксис

php
public static function forRepository(RepositoryInterface $repository): self

Параметры

$repository — репозиторий, чьё соединение определяет диалект.

Возвращает

TextMatch — конкретный режим; Lower, если конфигурацию разрешить не удалось.

Пример

php
$m = TextMatch::forRepository($repo);

$repo->andWhere(Qb::clip(Qb::or(
  $m->match('a.title', "%$s%"),
  $m->match('a.body',  "%$s%"),
)));

TextMatch::match()

Строит условие совпадения по шаблону в этом режиме.

Синтаксис

php
public function match(string $column, string $pattern, bool $negated = false): Qb

Параметры

$column — доверенное SQL-выражение колонки. Как и везде, оно приходит из вашего кода.

$pattern — шаблон LIKE вместе с подстановочными знаками: их расставляете вы, метод ничего не дописывает.

$negated — построить отрицание (NOT LIKE / NOT ILIKE).

Возвращает

Qb — условие со связанным значением.

Ошибки

LogicException — при вызове на TextMatch::Auto. Это режим-указание, а не написание: его нужно разрешить до построения SQL.

Пример

php
echo TextMatch::Lower->match('a.title', '%Winter%')->getQuery();

Результат

sql
lower(a.title) LIKE :iqb0

Особенности режима Lower

Lower сворачивает регистр с обеих сторон: колонку — функцией SQL lower(), шаблон — через mb_strtolower() в PHP. Отсюда два следствия.

Он единственный чинит не-ASCII. В SQLite LIKE без ICU сравнивает без учёта регистра только латиницу: Ünïcode и ünïcode для него разные строки. Свёртка через lower() решает это.

php
// значение, ушедшее в байнд
TextMatch::Like->match('a.title',  '%WiNTeR Ünïcode%');   // '%WiNTeR Ünïcode%'
TextMatch::Lower->match('a.title', '%WiNTeR Ünïcode%');   // '%winter ünïcode%'

Две реализации lower() могут разойтись. Свёртка в PHP и свёртка в базе используют разные таблицы регистров и в редких локалях дают разный результат. Если это важно, задайте ILike или Like явно.

Индексы

ILIKE 'x%' не использует btree-индекс никогда, а lower(col) LIKE 'x%' — использует, если для lower(col) создан функциональный индекс. Для contains разница не имеет значения: шаблон с ведущим % неиндексируем в любой СУБД. Подробнее — в Диалектах и индексах.

Что дальше