# Clause API

> Built-in comparisons, clause inputs and value types, and custom clause validation and execution.

Source: https://docs.inertiax.dev/table/reference/clause/

A clause is one comparison a filter offers, with the input it takes: no value, one value, a list, or a range. Custom clauses use `InertiaX\Table\Filters\Clauses\Clause::make(string $key, ?string $label = null)`. Custom keys must be namespaced, such as `acme/last-days`.

## Built-in comparisons

| Keys                                                                                       | Input                                               | Value types                          |
| ------------------------------------------------------------------------------------------ | --------------------------------------------------- | ------------------------------------ |
| `equals`, `not-equals`                                                                     | One value; a list with `multiple()` (`select` only) | Every built-in value type            |
| `contains`, `not-contains`, `starts-with`, `not-starts-with`, `ends-with`, `not-ends-with` | One value                                           | `text`                               |
| `greater-than`, `greater-than-or-equal`, `less-than`, `less-than-or-equal`                 | One value                                           | `number`                             |
| `between`, `not-between`                                                                   | A `[from, to]` range                                | `number`, `date`, `datetime`, `time` |
| `before`, `after`                                                                          | One value                                           | `date`, `datetime`, `time`           |
| `is-set`, `is-not-set`                                                                     | None; labelled **Is not empty** and **Is empty**    | Any filter                           |

A built-in clause given another value type fails when the table is prepared, with a `FilterDefinitionException` such as `clause [greater-than] does not support value type [text]`. A custom value type takes only custom clauses, `is-set`, and `is-not-set`.

The corresponding `ClauseType` enum is in `InertiaX\Table\Filters\Clauses\Enums`. For example, `ClauseType::Contains->make()` creates the built-in contains clause. Text-pattern clauses support `caseSensitive(?bool $value = true)`; the default comes from `Filter::$defaultCaseSensitive`, initially false. Database comparison behavior can vary by collation.

## Inputs

A clause takes one value of its filter’s value type unless it says otherwise:

| Method                               | Input                                                                            |
| ------------------------------------ | -------------------------------------------------------------------------------- |
| `valueType(ValueType\|string $type)` | Its own value type: a built-in value type or `vendor/name`                       |
| `withoutValue()`                     | No value                                                                         |
| `range()`                            | A `[from, to]` pair                                                              |
| `multiple()`                         | A non-empty list                                                                 |
| `options(array $options)`            | The values a `select` input offers; without them, a `SelectFilter`’s own options |

The last of `withoutValue()`, `range()`, and `multiple()` wins. `multiple()` takes `select` values only, and `range()` takes `number`, `date`, `datetime`, `time`, or a custom value type. `options()` needs the `select` value type. Other combinations fail when the table is prepared, for example with `clause [acme/x] cannot take a range value of type [text]`.

## Value types

| Value type    | Accepted value                                                    |
| ------------- | ----------------------------------------------------------------- |
| `text`        | String                                                            |
| `number`      | Finite number                                                     |
| `boolean`     | Boolean                                                           |
| `select`      | String, number, or Boolean                                        |
| `date`        | `YYYY-MM-DD`                                                      |
| `datetime`    | Local `YYYY-MM-DDTHH:mm[:ss[.fraction]]`; a space may replace `T` |
| `time`        | `HH:mm[:ss[.fraction]]`                                           |
| `vendor/name` | Any JSON value except `null`                                      |

`ValueType` is in `InertiaX\Table\Filters\Enums`. Fractions contain one to six digits. Invalid dates fail, and so do ranges of a built-in value type whose start exceeds their end. Accepted temporal strings retain their submitted form in public state; comparisons use a separately normalized value.

`null` is never a value. A clause without input takes `null`, and every other clause rejects it. Use `is-not-set` to match missing values.

## Custom methods

| Method                                                               | Contract                                          |
| -------------------------------------------------------------------- | ------------------------------------------------- |
| `modifyUsing(Closure $modifier)`                                     | Append an input transformation                    |
| `validateUsing(?Closure $validator)`                                 | Return Boolean acceptance of the normalized value |
| `applyUsing(?Closure $callback, DataSourceKind\|string ...$sources)` | Apply the comparison to a supported source        |
| `supports(DataSourceKind\|string ...$sources)`                       | Declare source kinds separately                   |

`DataSourceKind` is in `InertiaX\Table\Enums`. See [Custom filters](https://docs.inertiax.dev/table/guide/custom-filters/) for complete comparison examples.

## Validation order

For a clause that takes a value, the server checks the value against the clause’s input, runs modifiers, checks the input again, validates temporal values and range order, then calls the custom validator. A custom value type accepts any value except `null`, so its clauses must validate the value they expect.

A clause without input accepts only `null` and skips modifiers and the custom validator. Validation happens before query execution.

## Execution contract

`applyUsing()` receives the source, the filter’s column as `$column`, the filter’s key as `$key`, and the validated value as `$value`. It may also request the filter, clause, and state through the callback evaluator. Declare supported source kinds explicitly.

Return the same source kind, or `null` to retain the incoming source. A custom clause that only implements SQL must declare only `DataSourceKind::Eloquent`. Unsupported sources and invalid return types fail with contextual exceptions.

For [custom sources](https://docs.inertiax.dev/table/guide/custom-sources/), the source receives the filter’s key and the validated value and performs the comparison. Use `modifyUsing()`/`validateUsing()` without an executor. Explicit `applyUsing()` conflicts with custom retrieval even with `supports(DataSourceKind::Custom)`; a direct Clause implementation’s local `apply()` method is never invoked on this path.

See [Callback context](https://docs.inertiax.dev/table/reference/callbacks/) for injectable arguments and resolution rules.
