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