Package · mui-data-grid

Match modes

MUI’s four string operators (contains, notContains, startsWith, endsWith) are case-insensitive by definition, and there is no portable way to write that in SQL. TextMatch is the enum that picks the spelling; by default it is resolved from your database driver.

Why this exists

The same operation is written three ways across three databases, and the difference is not cosmetic:

Database Case-insensitive match What ILIKE does
PostgreSQL col ILIKE :v works
MySQL / MariaDB col LIKE :v (with a *_ci collation) syntax error
SQLite col LIKE :v (ASCII case) syntax error

So on MySQL and SQLite ILIKE is not “slower” or “less precise” — it is a broken query. The spelling therefore cannot be baked into the library: it belongs to the connection, not to the grid.

Values

Case SQL for contains For
TextMatch::Auto resolved from the repository’s driver the default
TextMatch::ILike col ILIKE :v PostgreSQL
TextMatch::Like col LIKE :v MySQL / SQLite — the collation decides the case
TextMatch::Lower lower(col) LIKE lower(:v) any database, including non-ASCII folding

The other nineteen operators are plain standard SQL; the mode does not touch them.

Automatic resolution

In Auto mode MuiGrid::wrap() resolves the spelling once per call, from the driver of the database the given repository talks to:

PDO driver Mode
pgsql ILike
mysql Like
sqlite Like
anything else Lower

The driver comes from the database config bound to the repository. By this point the config is already registered with the connection pool and sits in its cache, so resolution costs one static-array lookup rather than a new connection. If the config cannot be resolved — no pool, an unregistered class, a test double — the portable Lower applies instead of an exception.

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

// on PostgreSQL → a.title ILIKE :iqb0
// on MySQL and SQLite → a.title LIKE :iqb0
// the code is identical

Pinning it yourself

Detection knows the driver but not your collation. An explicit mode is needed in three cases: a binary MySQL collation where LIKE is case-sensitive, an exotic driver, or building a condition without a live connection pool.

For the whole schema

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

For one column

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

A column’s mode overrides the schema’s. TextMatch::Auto on a column means “inherit from the schema” — the same as not calling the method.

Methods

TextMatch::forDriver()

Returns the concrete mode for a PDO driver name.

Syntax

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

Parameters

$driver — the driver name, e.g. pgsql, mysql, sqlite. Case-insensitive.

Returns

TextMatch — a concrete mode; Lower for an unrecognised driver.

TextMatch::forRepository()

Returns the concrete mode for the database a repository talks to.

Useful beyond the grid: if you build your own search condition, the same call keeps it portable — see Filters outside the grid.

Syntax

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

Parameters

$repository — the repository whose connection decides the dialect.

Returns

TextMatch — a concrete mode; Lower if the config could not be resolved.

Example

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

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

TextMatch::match()

Builds a pattern-match condition in this mode.

Syntax

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

Parameters

$column — a trusted SQL column expression. As everywhere, it comes from your code.

$pattern — the LIKE pattern including its wildcards: you place them, the method adds nothing.

$negated — build the negated form (NOT LIKE / NOT ILIKE).

Returns

Qb — the condition with the value bound.

Errors

LogicException — when called on TextMatch::Auto. That case is an instruction, not a spelling: it must be resolved before SQL is built.

Example

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

Result

sql
lower(a.title) LIKE :iqb0

Notes on Lower

Lower folds both sides: the column with SQL lower(), the pattern with PHP’s mb_strtolower(). Two things follow.

It is the only mode that fixes non-ASCII. In SQLite without ICU, LIKE ignores case for Latin letters only: Ünïcode and ünïcode are different strings to it. Folding with lower() solves that.

php
// the value that ends up bound
TextMatch::Like->match('a.title',  '%WiNTeR Ünïcode%');   // '%WiNTeR Ünïcode%'
TextMatch::Lower->match('a.title', '%WiNTeR Ünïcode%');   // '%winter ünïcode%'

Two lower() implementations can disagree. PHP’s folding and the database’s use different case tables and diverge in rare locales. If that matters, pin ILike or Like explicitly.

Indexes

ILIKE 'x%' never uses a btree index, while lower(col) LIKE 'x%' does — provided a functional index on lower(col) exists. For contains the point is moot: a leading % is unindexable on every database. More in Dialects & indexes.

Next steps