You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: add cross-item validation with field-level error paths
Document the nested error structure pattern for cross-item array validation (uniqueness, relationships) that enables field-level error paths like symlinks.2.destination. Added examples to array-validation, custom-validation, and error-handling guides. Updated planning docs with uniqueField() feature. Updated llms.txt for AI agent reference.
-[x]**`contains()`** - Validates array contains specific item or item matching validator
265
+
-[ ]**`uniqueField(string $fieldName, ?string $message = null)`** - Validates uniqueness of a specific field across array items with field-level error paths (e.g., `symlinks.2.destination`); convenience wrapper around `satisfies()` for common use case
-[x]**`nullifyEmpty()`** - Convert empty arrays to null (already implemented!)
269
270
-[x]**`filterEmpty()`** - Remove empty/null values and reindex automatically (already implemented!)
270
271
271
272
### Removed from Scope
272
-
-~~`unique()`~~ -- Use `array_unique()` or Laravel Collections (complex deduplication logic)
273
+
-~~`unique()`~~ -- Use `array_unique()` or Laravel Collections (complex deduplication logic). Note: `uniqueField()` validates uniqueness of a specific field across array items, which is different from deduplicating the array itself.
273
274
-~~`flatten()`~~ -- Complex recursive logic, use Laravel Collections or `array_merge_recursive()`
Copy file name to clipboardExpand all lines: TASKS.md
+3Lines changed: 3 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,3 +22,6 @@ Public repo; keep this list short (about 7) and up to date. Rules: no numbering
22
22
23
23
- Property-based tests
24
24
- Add a small property-based test set for key validators (string patterns, numeric constraints) to harden edge cases and catch regressions.
25
+
26
+
-`uniqueField()` array validation method
27
+
- Add `uniqueField(string $fieldName, ?string $message = null)` to ArrayValidator for validating uniqueness of nested fields across array items; convenience wrapper around `satisfies()` that automatically handles nested error structure for field-level error paths (e.g., `symlinks.2.destination`); common use case that deserves convenience method similar to Laravel's `distinct` rule.
Copy file name to clipboardExpand all lines: docs/guides/array-validation.md
+98Lines changed: 98 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -458,6 +458,104 @@ try {
458
458
}
459
459
```
460
460
461
+
## Cross-Item Validation
462
+
463
+
For validations that need to check relationships across multiple array items (like uniqueness), use `satisfies()` on the array validator. To attach errors to specific fields within items, use a nested error structure.
464
+
465
+
### Uniqueness Validation with Field-Level Errors
466
+
467
+
When validating uniqueness of a nested field (e.g., `destination` in each symlink), structure errors to attach them to the specific field:
// ['path' => 'symlinks.2.destination', 'message' => "Destination '/same/path' is not unique (also used at index 0)"]
537
+
// ]
538
+
}
539
+
```
540
+
541
+
**Key Pattern:** Structure errors as `[arrayIndex => [fieldName => [errorMessage]]]` to get field-level error paths. The flattening logic automatically builds paths like `symlinks.2.destination` from this nested structure.
542
+
543
+
### Simple Uniqueness Check (Array-Level Error)
544
+
545
+
If you don't need field-level errors, you can use a simpler approach that returns a single error at the array level:
- Validators that frequently receive specification updates or new variants
289
289
- Production applications requiring comprehensive validation features
290
290
291
+
## Cross-Item Array Validation
292
+
293
+
For validations that need to check relationships across multiple array items (like uniqueness, ordering, or dependencies), use `satisfies()` on the array validator. This runs **after** item validation, so you receive the validated data structure.
294
+
295
+
### Uniqueness Validation with Field-Level Errors
296
+
297
+
When validating uniqueness of a nested field across array items, structure errors to attach them to specific fields within items:
// This creates field-level error paths: 'items.2.id'
330
+
$errors = [];
331
+
foreach ($duplicates as $id => $indices) {
332
+
foreach (array_slice($indices, 1) as $idx) {
333
+
$errors[$idx] = [
334
+
'id' => ["ID {$id} is not unique (also used at index {$indices[0]})"]
335
+
];
336
+
}
337
+
}
338
+
339
+
throw new ValidationException($errors);
340
+
}
341
+
)
342
+
]);
343
+
344
+
$input = [
345
+
'items' => [
346
+
['id' => 1, 'name' => 'First'],
347
+
['id' => 2, 'name' => 'Second'],
348
+
['id' => 1, 'name' => 'Duplicate'], // Duplicate ID
349
+
],
350
+
];
351
+
352
+
try {
353
+
$schema->validate($input);
354
+
} catch (ValidationException $e) {
355
+
$flattened = $e->getFlattenedErrors();
356
+
// [
357
+
// ['path' => 'items.2.id', 'message' => 'ID 1 is not unique (also used at index 0)']
358
+
// ]
359
+
}
360
+
```
361
+
362
+
**Key Pattern:** Structure errors as `[arrayIndex => [fieldName => [errorMessage]]]` to get field-level error paths. The error flattening logic automatically builds paths like `items.2.id` from this nested structure.
363
+
364
+
### Other Cross-Item Validations
365
+
366
+
This pattern works for any cross-item validation:
367
+
368
+
```php
369
+
// Validate that items are in ascending order by 'order' field
**Cross-Item Validation:** Use `satisfies()` on array validator for uniqueness/relationships. Structure errors as `[index => [field => [message]]]` to get field-level paths (e.g., `symlinks.2.destination`). Runs after item validation, receives validated data structure.
0 commit comments