Operators
The operator is the middle part of a filter item: { field, operator, value }. This page
collects every supported operator, the SQL each turns into, and the table of which ones a
column accepts depending on its declared type.
The MGOperator enum
The strings are exactly what MUI X DataGrid sends; forward them from the UI unchanged. The cast to the enum happens during request hydration: an unknown string is rejected before the request reaches the schema.
| Category | Operator | Enum case |
|---|---|---|
| String | contains |
MGOperator::CONTAINS |
notContains |
MGOperator::NOT_CONTAINS |
|
startsWith |
MGOperator::STARTS_WITH |
|
endsWith |
MGOperator::ENDS_WITH |
|
| Equality | equals |
MGOperator::EQUALS |
is |
MGOperator::IS |
|
not |
MGOperator::NOT |
|
= |
MGOperator::EQ |
|
!= |
MGOperator::NEQ |
|
| Comparison | > |
MGOperator::GT |
>= |
MGOperator::GTE |
|
< |
MGOperator::LT |
|
<= |
MGOperator::LTE |
|
| Date | after |
MGOperator::AFTER |
onOrAfter |
MGOperator::ON_OR_AFTER |
|
before |
MGOperator::BEFORE |
|
onOrBefore |
MGOperator::ON_OR_BEFORE |
|
| Set | isAnyOf |
MGOperator::IS_ANY_OF |
| Emptiness | isEmpty |
MGOperator::IS_EMPTY |
isNotEmpty |
MGOperator::IS_NOT_EMPTY |
The date operators are synonyms of the comparison ones: they exist because the table’s UI names them differently for date columns, while the SQL comes out the same.
Operator → SQL
| Operator | SQL |
|---|---|
contains |
col LIKE :v with the value %v% * |
notContains |
col NOT LIKE :v with the value %v% * |
startsWith |
col LIKE :v with the value v% * |
endsWith |
col LIKE :v with the value %v * |
equals · is · = |
col = :v |
not · != |
col != :v |
> · after |
col > :v |
>= · onOrAfter |
col >= :v |
< · before |
col < :v |
<= · onOrBefore |
col <= :v |
isAnyOf |
col IN (:v0, :v1, …) — an empty set yields an empty condition |
isEmpty |
col IS NULL |
isNotEmpty |
col IS NOT NULL |
* The first four operators are case-insensitive, and SQL spells that differently per
database: ILIKE in PostgreSQL, LIKE in MySQL and SQLite, lower(col) LIKE lower(:v) as
the portable form. The spelling is chosen automatically — see
Match modes.
The value always travels as a bound parameter: the % wildcards are added on the PHP side,
never by string concatenation in SQL.
The FilterType enum
The type is declared on a column via filterable() and acts as a gate: an operator outside
the set is rejected with 400 before it reaches SQL. That protects the query from a merely
misconfigured UI as much as from a malicious one.
FilterType |
Allowed operators |
|---|---|
String |
contains, notContains, startsWith, endsWith, equals, is, not, =, !=, isAnyOf, isEmpty, isNotEmpty |
Number |
equals, is, not, =, !=, >, >=, <, <=, isAnyOf, isEmpty, isNotEmpty |
Boolean |
is, equals, =, isEmpty, isNotEmpty |
Date |
is, not, equals, =, !=, after, onOrAfter, before, onOrBefore, isEmpty, isNotEmpty |
The same matrix seen from the operator’s side:
| Operator | String |
Number |
Boolean |
Date |
|---|---|---|---|---|
contains · notContains · startsWith · endsWith |
✓ | — | — | — |
equals · is · = |
✓ | ✓ | ✓ | ✓ |
not · != |
✓ | ✓ | — | ✓ |
> · >= · < · <= |
— | ✓ | — | — |
after · onOrAfter · before · onOrBefore |
— | — | — | ✓ |
isAnyOf |
✓ | ✓ | — | — |
isEmpty · isNotEmpty |
✓ | ✓ | ✓ | ✓ |
Choose the type by how the value behaves in SQL
The type here is not about the PHP type but about which comparisons make sense. A status
column compared only for equality and set membership is sensibly a String, even if the
database stores a smallint. A timestamp is a Date, which gets you the range operators and
keeps contains away.
A rejection, end to end
GridColumn::for('views', 'a.views')->filterable(FilterType::Number);{ "field": "views", "operator": "contains", "value": "1" }{ "message": "Operator 'contains' is not allowed for field 'views'" }Combining conditions: AND and OR
The filter model’s logicOperator decides how items are joined. The value is validated at
hydration: only and and or are accepted, in any case, defaulting to and.
{
"filterModel": {
"logicOperator": "or",
"items": [
{ "field": "title", "operator": "contains", "value": "sql" },
{ "field": "authorName", "operator": "contains", "value": "sql" }
]
}
}Result
((a.title LIKE :iqb0 OR au.name LIKE :iqb1))The outer parentheses are deliberate: without them the OR would blend into your base
WHERE through AND and change the meaning of the query.
Edge cases
| Case | Behavior |
|---|---|
items is empty |
no condition is added, the base WHERE is untouched |
isAnyOf with an empty array |
that item is skipped, the others apply |
| every item produced an empty condition | no condition is added |
value missing for isEmpty / isNotEmpty |
fine — those operators need none |
| unknown operator string | 400 during request hydration |
| field not declared in the schema | 400 from the schema |
Next steps
- Match modes — how the four case-insensitive operators are spelled.
- Custom filters — overriding the mapping for a specific column.
- API reference —
filterable()and its neighbours.