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
Copy file name to clipboardExpand all lines: CHANGELOG.md
+4Lines changed: 4 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
4
4
5
5
## [Unreleased]
6
6
7
+
### Added
8
+
9
+
-`uniqueField(string $fieldName, ?string $message = null)` on `ArrayValidator` for cross-item uniqueness validation of a nested field; convenience wrapper around `satisfies()` that produces field-level error paths (e.g., `symlinks.2.destination`) by structuring errors as `[index => [fieldName => [message]]]`; skips items where the field is null or missing; works with both associative arrays and objects; uses strict comparison via `serialize()` to distinguish types
Public repo; keep this list short (about 7) and up to date. Rules: no numbering (keeps churn low), prune completed items, and replace them with the next priority. Contributors: pick one task, keep PRs focused, and update the list as things land.
3
+
Public repo; keep this list short (about 7) and up to date. No numbering. Prune completed items and replace with the next priority. Contributors: pick one task, keep PRs focused, update the list as things land.
4
4
5
5
- Structured error codes
6
-
- Add programmatic error codes to validation errors (e.g., 'STRING_TOO_SHORT', 'INVALID_EMAIL') for better error handling and i18n support; keep backward compatibility.
6
+
7
+
Add programmatic error codes to validation errors (e.g., STRING_TOO_SHORT, INVALID_EMAIL) for better error handling and i18n support; keep backward compatibility.
7
8
8
9
- Mutation testing pilot
9
-
- Wire Infection (or similar) to the test suite, add baseline config, and document how to run it locally for enhanced test quality verification.
10
+
11
+
Wire Infection (or similar) to the test suite, add baseline config, and document how to run it locally for enhanced test quality verification.
10
12
11
13
- Property-based tests
12
-
- Add a small property-based test set for key validators (string patterns, numeric constraints) to harden edge cases and catch regressions.
13
14
14
-
-`uniqueField()` array validation method
15
-
- 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.
15
+
Add a small property-based test set for key validators (string patterns, numeric constraints) to harden edge cases and catch regressions.
16
+
17
+
- Deprecation cleanup
18
+
19
+
Remove or finalize deprecated methods (addValidation, allOf, anyOf, not) before v1.0; update call sites and docs.
Validates that a nested field is unique across all array items. Produces field-level error paths (e.g., `symlinks.2.destination`) automatically. Items where the field is null or missing are skipped. Works with associative arrays and objects.
Copy file name to clipboardExpand all lines: docs/guides/array-validation.md
+13-50Lines changed: 13 additions & 50 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -477,11 +477,9 @@ try {
477
477
478
478
## Cross-Item Validation
479
479
480
-
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.
480
+
### uniqueField() for Uniqueness
481
481
482
-
### Uniqueness Validation with Field-Level Errors
483
-
484
-
When validating uniqueness of a nested field (e.g., `destination` in each symlink), structure errors to attach them to the specific field:
482
+
Use `uniqueField()` to validate that a nested field is unique across all array items. It produces field-level error paths (e.g., `symlinks.2.destination`) automatically. Items where the field is null or missing are skipped.
// This creates field-level error paths: 'symlinks.2.destination'
521
-
$errors = [];
522
-
foreach ($duplicates as $dest => $indices) {
523
-
// Mark all but the first occurrence as duplicates
524
-
foreach (array_slice($indices, 1) as $idx) {
525
-
$errors[$idx] = [
526
-
'destination' => [
527
-
"Destination '{$dest}' is not unique (also used at index {$indices[0]})"
528
-
]
529
-
];
530
-
}
531
-
}
532
-
533
-
throw new ValidationException($errors);
534
-
},
535
-
'All destinations must be unique'
536
-
)
491
+
->uniqueField('destination')
537
492
->required(),
538
493
]);
539
494
@@ -550,12 +505,20 @@ try {
550
505
} catch (ValidationException $e) {
551
506
$flattened = $e->getFlattenedErrors();
552
507
// [
553
-
// ['path' => 'symlinks.2.destination', 'message' => "Destination '/same/path' is not unique (also used at index 0)"]
508
+
// ['path' => 'symlinks.2.destination', 'message' => "Value '/same/path' is not unique (also used at index 0)"]
554
509
// ]
555
510
}
556
511
```
557
512
558
-
**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.
513
+
Custom message:
514
+
515
+
```php
516
+
->uniqueField('destination', 'Destination must be unique across all symlinks')
517
+
```
518
+
519
+
### Custom Cross-Item Validation with satisfies()
520
+
521
+
For validations beyond uniqueness (ordering, dependencies, custom logic), use `satisfies()` on the array validator. Structure errors as `[arrayIndex => [fieldName => [errorMessage]]]` to get field-level paths.
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.
// This creates field-level error paths: 'items.2.id'
332
-
$errors = [];
333
-
foreach ($duplicates as $id => $indices) {
334
-
foreach (array_slice($indices, 1) as $idx) {
335
-
$errors[$idx] = [
336
-
'id' => ["ID {$id} is not unique (also used at index {$indices[0]})"]
337
-
];
338
-
}
339
-
}
340
-
341
-
throw new ValidationException($errors);
342
-
}
343
-
)
308
+
->uniqueField('id'),
344
309
]);
345
310
346
311
$input = [
@@ -356,12 +321,12 @@ try {
356
321
} catch (ValidationException $e) {
357
322
$flattened = $e->getFlattenedErrors();
358
323
// [
359
-
// ['path' => 'items.2.id', 'message' => 'ID 1 is not unique (also used at index 0)']
324
+
// ['path' => 'items.2.id', 'message' => "Value '1' is not unique (also used at index 0)"]
360
325
// ]
361
326
}
362
327
```
363
328
364
-
**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.
329
+
Use `satisfies()` when you need custom duplicate logic, custom messages per value, or cross-field uniqueness. See the [Array Validation Guide](array-validation.md#custom-cross-item-validation-with-satisfies) for the manual `satisfies()` pattern.
// ['path' => 'symlinks.2.destination', 'message' => "'/path1' is not unique"]
286
+
// ['path' => 'symlinks.2.destination', 'message' => "Value '/path1' is not unique (also used at index 0)"]
319
287
// ]
320
288
}
321
289
```
322
290
323
-
**Key Pattern:** Structure errors as `[arrayIndex => [fieldName => [errorMessage]]]` to get field-level error paths in flattened output.
291
+
For custom cross-item logic, use `satisfies()` and structure errors as `[arrayIndex => [fieldName => [errorMessage]]]` to get field-level paths in flattened output.
**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.
142
+
**Cross-Item Validation:** `uniqueField()` validates a nested field is unique across items with field-level error paths (e.g., `symlinks.2.destination`). For custom cross-item logic, use `satisfies()` directly -- structure errors as `[index => [field => [message]]]` for field-level paths. Both run after item validation and receive validated data.
0 commit comments