Rule::forEach()
Apply dynamic validation rules to each element in a nested array — with access to the item's value and fully-expanded attribute name.
Overview
When validating arrays of data, you sometimes need rules that depend on each item's value. Rule::forEach() lets you return a dynamic set of rules for every element in a nested array, with full access to each item's value and attribute path.
Usage
use App\Rules\HasPermission;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class UpdateCompaniesRequest extends FormRequest
{
public function rules(): array
{
return [
'companies' => ['required', 'array'],
'companies.*.id' => Rule::forEach(function (
string|null $value,
string $attribute,
) {
return [
Rule::exists(Company::class, 'id'),
new HasPermission('manage-company', $value),
];
}),
];
}
}
The closure receives two arguments:
$value— the current element's value (e.g., the company ID)$attribute— the fully-expanded attribute name (e.g.,companies.0.id)
Real-World Example
Validate that each line item references a product the user can actually purchase, with quantity not exceeding that product's stock:
use App\Models\Product;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class StoreOrderRequest extends FormRequest
{
public function rules(): array
{
return [
'items' => ['required', 'array', 'min:1'],
'items.*.product_id' => Rule::forEach(function ($value) {
return [
'required',
Rule::exists('products', 'id')->where('is_active', true),
];
}),
'items.*.quantity' => Rule::forEach(function ($value, $attribute) {
$index = explode('.', $attribute)[1];
$productId = request("items.{$index}.product_id");
$maxStock = Product::find($productId)?->stock ?? 0;
return [
'required',
'integer',
'min:1',
"max:{$maxStock}",
];
}),
];
}
}
When to Use
- Validating arrays where each item needs rules based on its own value or sibling fields
- Permission checks per item in a batch operation
- Dynamic
max,exists, oruniquerules that vary per array element - Any
companies.*oritems.*pattern where static wildcard rules aren't enough