Better Route Resource write schema
Use writeSchema() to validate and normalize Resource create/update payload fields. payloadSchema() is an exact alias.
Resource::make('articles')
->allow(['create', 'update'])
->fields(['id', 'title', 'status', 'priority', 'published_at'])
->writeSchema([
'title' => [
'type' => 'string',
'required' => true,
'sanitize' => 'text',
'minLength' => 1,
'maxLength' => 180,
],
'status' => [
'type' => 'enum',
'values' => ['draft', 'published'],
],
'priority' => ['type' => 'int', 'min' => 0, 'max' => 100],
'published_at' => ['type' => 'date', 'nullable' => true],
]);
The enum allowlist is flat: use ['type' => 'enum', 'values' => [...]]. Do not nest values under an enum key.
Rule contract
- Type strings may be supplied directly, such as
'title' => 'string'.
- Supported coercion types are
int/integer, float/number, bool/boolean, string, date, email, url, enum, array, object, and mixed.
required is enforced only on create; updates may be partial but must contain at least one writable field.
nullable: true accepts null; otherwise null fails validation.
- String constraints are
minLength, maxLength, and regex. Numeric constraints are min and max.
email and url use PHP validation after coercion/sanitization. date is only string coercion; validate date format with regex or a callable sanitizer/other domain layer.
- Sanitizers are
text, email, key, url, or a callable receiving (value, field). An unknown sanitizer string leaves the value unchanged, so never treat arbitrary names as validation.
- Callable sanitizers transform values; they are not authorization checks and must return a value compatible with subsequent constraints.
Resource writable fields come from configured fields minus the ID field. Unknown payload keys fail with 400 validation_failed; they are not silently dropped. A field denied by fieldPolicy also fails, rather than disappearing from the write.
Validation errors use the standard error envelope with details.fieldErrors. Boolean fieldPolicy: false produces a 400 non-writable validation error; failed capabilities or policy callbacks produce 403 errors. Follow br-resource-policy for authorization.
Checks
- Test unknown, empty, null, malformed, boundary, and coerced values.
- Test required fields separately on create and partial update.
- Test enum values with strict type expectations; enum values are compared strictly after string coercion.
- Anchor regexes and set explicit maximum lengths before expensive domain processing.
- Do not add
patch to allow(); the Resource action name is update, even though its route handles update semantics.
Source reference: src/Resource/Resource.php (readPayload, coercePayloadValue, assertValueConstraints, validationError).
References
1---2name: br-write-schema3description: Configure Better Route 1.1 Resource writeSchema or payloadSchema validation for create and update payloads. Use when defining writable fields, coercion, sanitization, required and nullable values, lengths, ranges, regexes, enums, or structured fieldErrors.4---56# Better Route Resource write schema78Use `writeSchema()` to validate and normalize Resource create/update payload fields. `payloadSchema()` is an exact alias.910```php11Resource::make('articles')12 ->allow(['create', 'update'])13 ->fields(['id', 'title', 'status', 'priority', 'published_at'])14 ->writeSchema([15 'title' => [16 'type' => 'string',17 'required' => true,18 'sanitize' => 'text',19 'minLength' => 1,20 'maxLength' => 180,21 ],22 'status' => [23 'type' => 'enum',24 'values' => ['draft', 'published'],25 ],26 'priority' => ['type' => 'int', 'min' => 0, 'max' => 100],27 'published_at' => ['type' => 'date', 'nullable' => true],28 ]);29```3031The enum allowlist is flat: use `['type' => 'enum', 'values' => [...]]`. Do not nest `values` under an `enum` key.3233## Rule contract3435- Type strings may be supplied directly, such as `'title' => 'string'`.36- Supported coercion types are `int`/`integer`, `float`/`number`, `bool`/`boolean`, `string`, `date`, `email`, `url`, `enum`, `array`, `object`, and `mixed`.37- `required` is enforced only on `create`; updates may be partial but must contain at least one writable field.38- `nullable: true` accepts `null`; otherwise `null` fails validation.39- String constraints are `minLength`, `maxLength`, and `regex`. Numeric constraints are `min` and `max`.40- `email` and `url` use PHP validation after coercion/sanitization. `date` is only string coercion; validate date format with `regex` or a callable sanitizer/other domain layer.41- Sanitizers are `text`, `email`, `key`, `url`, or a callable receiving `(value, field)`. An unknown sanitizer string leaves the value unchanged, so never treat arbitrary names as validation.42- Callable sanitizers transform values; they are not authorization checks and must return a value compatible with subsequent constraints.4344Resource writable fields come from configured `fields` minus the ID field. Unknown payload keys fail with `400 validation_failed`; they are not silently dropped. A field denied by `fieldPolicy` also fails, rather than disappearing from the write.4546Validation errors use the standard error envelope with `details.fieldErrors`. Boolean `fieldPolicy: false` produces a 400 non-writable validation error; failed capabilities or policy callbacks produce 403 errors. Follow `br-resource-policy` for authorization.4748## Checks4950- Test unknown, empty, null, malformed, boundary, and coerced values.51- Test required fields separately on create and partial update.52- Test enum values with strict type expectations; enum values are compared strictly after string coercion.53- Anchor regexes and set explicit maximum lengths before expensive domain processing.54- Do not add `patch` to `allow()`; the Resource action name is `update`, even though its route handles update semantics.5556Source reference: `src/Resource/Resource.php` (`readPayload`, `coercePayloadValue`, `assertValueConstraints`, `validationError`).5758## References5960- Official documentation: <https://lonsdale201.github.io/better-docs/docs/better-route/agents>