$npx -y skills add Lonsdale201/wp-agent-skills --skill br-write-schemaConfigure 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.
| 1 | # Better Route Resource write schema |
| 2 | |
| 3 | Use `writeSchema()` to validate and normalize Resource create/update payload fields. `payloadSchema()` is an exact alias. |
| 4 | |
| 5 | ```php |
| 6 | Resource::make('articles') |
| 7 | ->allow(['create', 'update']) |
| 8 | ->fields(['id', 'title', 'status', 'priority', 'published_at']) |
| 9 | ->writeSchema([ |
| 10 | 'title' => [ |
| 11 | 'type' => 'string', |
| 12 | 'required' => true, |
| 13 | 'sanitize' => 'text', |
| 14 | 'minLength' => 1, |
| 15 | 'maxLength' => 180, |
| 16 | ], |
| 17 | 'status' => [ |
| 18 | 'type' => 'enum', |
| 19 | 'values' => ['draft', 'published'], |
| 20 | ], |
| 21 | 'priority' => ['type' => 'int', 'min' => 0, 'max' => 100], |
| 22 | 'published_at' => ['type' => 'date', 'nullable' => true], |
| 23 | ]); |
| 24 | ``` |
| 25 | |
| 26 | The enum allowlist is flat: use `['type' => 'enum', 'values' => [...]]`. Do not nest `values` under an `enum` key. |
| 27 | |
| 28 | ## Rule contract |
| 29 | |
| 30 | - Type strings may be supplied directly, such as `'title' => 'string'`. |
| 31 | - Supported coercion types are `int`/`integer`, `float`/`number`, `bool`/`boolean`, `string`, `date`, `email`, `url`, `enum`, `array`, `object`, and `mixed`. |
| 32 | - `required` is enforced only on `create`; updates may be partial but must contain at least one writable field. |
| 33 | - `nullable: true` accepts `null`; otherwise `null` fails validation. |
| 34 | - String constraints are `minLength`, `maxLength`, and `regex`. Numeric constraints are `min` and `max`. |
| 35 | - `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. |
| 36 | - 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. |
| 37 | - Callable sanitizers transform values; they are not authorization checks and must return a value compatible with subsequent constraints. |
| 38 | |
| 39 | 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. |
| 40 | |
| 41 | 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. |
| 42 | |
| 43 | ## Checks |
| 44 | |
| 45 | - Test unknown, empty, null, malformed, boundary, and coerced values. |
| 46 | - Test required fields separately on create and partial update. |
| 47 | - Test enum values with strict type expectations; enum values are compared strictly after string coercion. |
| 48 | - Anchor regexes and set explicit maximum lengths before expensive domain processing. |
| 49 | - Do not add `patch` to `allow()`; the Resource action name is `update`, even though its route handles update semantics. |
| 50 | |
| 51 | Source reference: `src/Resource/Resource.php` (`readPayload`, `coercePayloadValue`, `assertValueConstraints`, `validationError`). |
| 52 | |
| 53 | ## References |
| 54 | |
| 55 | - Official documentation: <https://lonsdale201.github.io/better-docs/docs/better-route/agents> |