Skip to content

Prefixing

The following classes support prefixing via the prefix() method:

The prefix() method

The signature of the prefix method looks as follows:

php
public function prefix(string $prefix): self

Trailing 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
<?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
<?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
<?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
<?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
<?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);
    }
}