---
title: "Validation"
description: "How the rules that process the data of a form are derived from its schema, and what each keyword of the schema validates"
type: "docs"
category: "doc"
tags: []
authors: [Anonymous]
date: "2026-10-15"
last_update: "2026-10-15"
time_minutes: 5
draft: false
unlisted: false
url: "https://www.derafu.dev/docs/ui/form/validation"
---

# Validation

`FormDataProcessor` processes the data that comes with a form: it casts, sanitizes, transforms and validates the value of each field, and gives back the processed data and the errors. The rules are the ones of [`derafu/data-processor`](https://www.derafu.dev/docs/data/data-processor). Nobody writes them for the common cases: they are **derived from the schema of the form**, and the `rules` section of the definition adds the rest.

```php
$result = $processor->process($form, $_POST);

if ($result->hasErrors()) {
    $errors = $result->getErrors();     // ['field' => ['message']]
    $form = $result->getForm();         // The form with its errors, to render it again.
} else {
    $data = $result->getProcessedData();
}
```

## Where the rules come from

For each field the processor joins three sources, in this order:

1. **The property of the schema**: its `type`, `format`, `pattern`, limits, `enum`, etc. (see below).
2. **The control of the UI schema**: a control of type `file` or `image` validates a file or an image, and a control of type `editor` removes the tags of the HTML.
3. **The `rules` section of the definition**: explicit rules by field.

```php
'rules' => [
    'name' => [
        'transform' => ['uppercase'],
        'validate' => ['min_length:3'],
    ],
],
```

The rules of a field are written in four categories, and are applied in this order: `cast`, `transform`, `sanitize`, `validate`. The explicit ones are joined to the derived ones like this:

- `cast`: the explicit one **replaces** the derived one. When the type of the cast changes, the derived sanitizers (for example `trim`, that is for strings) are dropped, and the explicit ones are kept.
- `sanitize`, `transform` and `validate`: the explicit ones are **added after** the derived ones.
- `required`: the fields that are required (see [Required fields](required-fields)) have it as the first rule of `validate`, once.

A field whose [rule of the UI schema](examples/rules) is not active (it is hidden, for example) is not validated and is not in the processed data, even if it came with the data.

## Objects

The properties of an object of the schema are fields of the form with the name of their path, `address.city`, and the data has them inside the object, as PHP reads a form (`address[city]` in the name of the control):

```php
'schema' => [
    'type' => 'object',
    'required' => ['contact'],
    'properties' => [
        'contact' => [
            'type' => 'object',
            'required' => ['address', 'zone'],
            'properties' => [
                'address' => ['type' => 'string'],
                'zone' => ['type' => 'string'],
                'city' => ['type' => 'string'],
            ],
        ],
    ],
],
'uischema' => [
    'type' => 'VerticalLayout',
    'elements' => [
        ['type' => 'Control', 'scope' => '#/properties/contact/properties/address'],
        ['type' => 'Control', 'scope' => '#/properties/contact/properties/zone'],
        ['type' => 'Control', 'scope' => '#/properties/contact/properties/city'],
    ],
],
```

```php
$result = $processor->process($form, ['contact' => ['address' => 'Street 1', 'zone' => 'North', 'city' => '']]);

$result->getProcessedData();  // ['contact' => ['address' => 'Street 1', 'zone' => 'North', 'city' => '']]
$result->getErrors();         // ['contact.zone' => [...]] when the zone is missing.
```

- The processed data and the data that comes back to the form have the same shape as the data that came.
- The errors have the name of the path of the field (`contact.zone`).
- A field is required by the `required` of **its own object**: `address` and `zone` are, `city` is not. The `required` of the root only asks for the key `contact`: it does not make any of its fields required.
- A key with the whole name (`'contact.address'`) is the value of that field too.
- The conditions of the [rules of the UI schema](examples/rules) can be on a field of an object (`#/properties/contact/properties/city`).

## What the schema validates

| In the schema | Rule |
|---|---|
| `type: string` | cast `string`; sanitize `trim` |
| `type: integer` | cast `integer`; validate `int` |
| `type: number` | cast `float`; validate `numeric` |
| `type: boolean` | cast `boolean` |
| `minLength`, `maxLength` | `min_length`, `max_length` |
| `minimum`, `maximum` (integer and number) | `gte`, `lte` |
| `minItems`, `maxItems`, `uniqueItems` | `min_items`, `max_items`, `unique` |
| `pattern` | `regex` (see below) |
| `enum`, or `oneOf` with `const` | `in` with the values |
| `contentMediaType: application/json` | `json` |
| `format` | see the table below |

A string with `format: email` is also transformed to lowercase.

### Formats

| `format` | Rule |
|---|---|
| `email` | `email` |
| `uri`, `url` | `url` |
| `date` | `date_format:Y-m-d` |
| `date-time` | `date_format:Y-m-d H:i:s` |
| `time` | `date_format:H:i:s` |
| `tel` | `regex` of telephone numbers |
| `hostname` | `hostname` |
| `ipv4`, `ipv6` | `ip` |
| `uuid` | `uuid` |
| `base64` | `base64` |
| `json` | `json` |
| `regex` | `regex_pattern`: the value must be a regular expression |
| another | `string` |

### Patterns

The `pattern` of JSON Schema is written without delimiters, like the browsers read the `pattern` attribute of HTML. PHP needs them, so the form gives the pattern the slash as its delimiter and escapes the slashes that it has, once: `^a/b$` and `^a\/b$` are the same pattern. The same conversion is used by the patterns of the types and by the conditions of the rules of the UI schema.

The regular expression is a PCRE one, the dialect of PHP. A pattern that can not be compiled is an error of the definition, not of the value: it is reported when the field is processed. A field with `format: regex` is the opposite: the **value** is the pattern, and it is invalid if it can not be compiled.

### Booleans

For JSON Schema, `required` says that the key must be in the data, not that it has a given value. A boolean that is only `required` is valid when it is checked and when it is not. To ask for a value, fix it in the schema:

```php
'terms' => [
    'type' => 'boolean',
    'title' => 'I accept the terms',
    'const' => true,          // or 'enum' => [true]
],
```

- `const: true` or `enum: [true]` validate with `accepted`: the value must be `true`.
- `const: false` or `enum: [false]` validate with `declined`: the value must be `false`.

A browser does not send a checkbox that is not checked, so a missing key is read as `false`, and the form that asks for `true` fails. The control has the native `required` too, see [Required fields](required-fields).

## Translated errors

The messages of the errors are translated when the processor has a translator, see [Translations](translations).



---
Last updated on 15/10/2026

