Prefixing
The following classes support prefixing via the prefix() method:
The prefix() method
The signature of the prefix method looks as follows:
public function prefix(string $prefix): selfTrailing dots
Trailing dots are automatically appended to the prefix, so passing collection.*. or collection.* produces the exact same result.
Immutability
Calling the prefix() method always returns a new instance of the class it is called on.
Deep prefixing
The prefix() method recursively updates the validation key across all nested items and underlying rule instances within the collection. This includes rule arguments that reference other fields, such as required_if.
For example, referencing another field within a rule automatically adapts to the new prefix:
<?php
declare(strict_types=1);
use LaraPkgs\Validation\Support\Facades\Validatable;
$collection = Validatable::collection(
Validatable::item('is_company')->required()->boolean(),
Validatable::item('vat_number')->requiredIf('is_company', true)
);
$validatorArguments = $collection->toValidatorArguments();
// [
// 'rules' => [
// 'is_company' => ['required', 'boolean'],
// 'vat_number' => ['required_if:is_company,true'],
// ],
// 'messages' => [],
// 'attributes' => [
// 'is_company' => 'is_company',
// 'vat_number' => 'vat_number'
// ]
// ]
$prefixed = $collection->prefix('billing');
$validatorArguments = $prefixed->toValidatorArguments();
// [
// 'rules' => [
// 'billing.is_company' => ['required', 'boolean'],
// 'billing.vat_number' => ['required_if:billing.is_company,true'],
// ],
// 'messages' => [],
// 'attributes' => [
// 'billing.is_company' => 'billing.is_company',
// 'billing.vat_number' => 'billing.vat_number'
// ]
// ]Examples
In client prefixing and merging
The OrderValidation class:
<?php
declare(strict_types=1);
namespace App\Validation;
use LaraPkgs\Validation\Validatable as BaseValidatable;
use LaraPkgs\Validation\ValidatableCollection;
use LaraPkgs\Validation\Support\Facades\Validatable;
final class OrderValidation extends BaseValidatable
{
protected function makeValidatableCollection(): ValidatableCollection
{
return Validatable::collection(
Validatable::item('customer_name')
->required()->string(),
Validatable::item('items')
->required()->array()->min(1),
);
}
}The OrderItemValidation class:
<?php
declare(strict_types=1);
namespace App\Validation;
use LaraPkgs\Validation\Validatable as BaseValidatable;
use LaraPkgs\Validation\ValidatableCollection;
use LaraPkgs\Validation\Support\Facades\Validatable;
final class OrderItemValidation extends BaseValidatable
{
protected function makeValidatableCollection(): ValidatableCollection
{
return Validatable::collection(
Validatable::item('product_id')
->required()->integer(),
Validatable::item('quantity')
->required()->integer()->min(1),
Validatable::item('is_discounted')
->required()->boolean(),
Validatable::item('discount_percentage')
->nullable()->requiredIf('is_discounted', true)->numeric()->min(0)->max(100)
->addMessages(['required_if' => 'The :attribute field is required if is_discounted is marked as true.'])
);
}
}The client code:
<?php
declare(strict_types=1);
use App\Validation\OrderValidation;
use App\Validation\OrderItemValidation;
$orderValidation = new OrderValidation();
$itemValidation = new OrderItemValidation()->prefix('items.*');
$validation = $orderValidation->merge($itemValidation);
$validatorArguments = $validation->getValidatableCollection()->toValidatorArguments();
// [
// 'rules' => [
// 'customer_name' => ['required', 'string'],
// 'items' => ['required', 'array', 'min:1'],
// 'items.*.product_id' => ['required', 'integer'],
// 'items.*.quantity' => ['required', 'integer', 'min:1'],
// 'items.*.is_discounted' => ['required', 'boolean'],
// 'items.*.discount_percentage' => [
// 'nullable',
// 'required_if:items.*.is_discounted,true',
// 'numeric',
// 'min:0',
// 'max:100',
// ],
// ],
// 'messages' => [
// "items.*.discount_percentage.required_if" => "The :attribute field is required if is_discounted is marked as true."
// ],
// 'attributes' => [
// 'customer_name' => 'customer_name',
// 'items' => 'items',
// 'items.*.product_id' => 'items.*.product_id',
// 'items.*.quantity' => 'items.*.quantity',
// 'items.*.is_discounted' => 'items.*.is_discounted',
// 'items.*.discount_percentage' => 'items.*.discount_percentage',
// ],
// ]
$data = [
'customer_name' => 'Jane Doe',
'items' => [
[
'product_id' => 10,
'quantity' => 2,
'is_discounted' => true,
'discount_percentage' => 15,
],
[
'product_id' => 12,
'quantity' => 1,
'is_discounted' => false,
'discount_percentage' => null,
],
],
];
$validated = $validation->validate($data);
// [
// 'customer_name' => 'Jane Doe',
// 'items' => [
// [
// 'product_id' => 10,
// 'quantity' => 2,
// 'is_discounted' => true,
// 'discount_percentage' => 15,
// ],
// [
// 'product_id' => 12,
// 'quantity' => 1,
// 'is_discounted' => false,
// 'discount_percentage' => null,
// ],
// ],
// ]In class prefixing and merging
The same result as in the previous example can be achieved by prefixing and merging the OrderItemValidation class in the makeValidatableCollection method of the OrderValidation class.
The updated OrderValidation class:
<?php
declare(strict_types=1);
namespace App\Validation;
use App\Validation\OrderItemValidation;
use LaraPkgs\Validation\Validatable as BaseValidatable;
use LaraPkgs\Validation\ValidatableCollection;
use LaraPkgs\Validation\Support\Facades\Validatable;
final class OrderValidation extends BaseValidatable
{
protected function makeValidatableCollection(): ValidatableCollection
{
$collection = Validatable::collection(
Validatable::item('customer_name')
->required()->string(),
Validatable::item('items')
->required()->array()->min(1),
);
$orderItemValidation = new OrderItemValidation()->prefix('items.*');
return $collection->merge($orderItemValidation);
}
}