A full stack artisan at work

Laravel Data & Validation

by Ruben Van Assche


Validation is complex, and we don't want to write it, yet having it within an application is super important. Laravel data tries to solve this problem by automatically writing validation rules and making it fun to add additional validation rules.

But to achieve this, we must make clear rules for validation in Laravel data. We've implemented many features in the previous years without thinking about the bigger picture, which created a lot of weird scenarios within the package.

That's why this spec was written. We've declared how validation works and will keep this spec as the guideline to implement the validation logic in Laravel data. Changes to the validation code should always be checked with the spec, and if required, the spec should be updated after discussion if it would be a worthwhile change.

This spec is opinionated, and that's with a reason. You may agree or disagree with it, but the plan is to stick to it. Certain decisions were made only to make the validation of this package work at a bare minimum. Some of these decisions might be strange, and you might think it would be valuable that feature A or B would be implemented. Rest assured, we thought this thing through! In the end, the Laravel validation system is not made to be generated semi-automatically. Using this spec, we try to make it run as smoothly as possible.

A small note about this document: This is a beta version of the spec. It has a lot of information about things laravel-data had in the past and some sections on why we've decided on a specific approach. When this spec is implemented, we will update it so it describes the whole process without extra information and gives a clear understanding of laravel-data's validation logic. Sections explaining design decisions would be stored in a validation design considerations document.

Some common terms

At which point is validation ran

Validation ALWAYS runs before a data object exists. If you want to validate data objects when they are created as PHP objects, then you should write the validation logic by yourself. There are a few reasons for this:

When do we run validation

Validation runs on a few different occasions:

On all other occasions, validation won't run. Which includes:

Mapping and naming

The names of properties in a data object can be mapped in two ways:

class DataObject extends Data
{
    #[MapName(input: 'first_name')]
    public string $firstName;
    
    #[MapInputName('last_name')]
    public string $lastName;
}

Additionally, a mapper can be used:

class DataObject extends Data { #[MapName(SnakeCaseMapper::class)] public string $firstName; #[MapInputName(SnakeCaseMapper::class)] public string $lastName; }

This is a quicker version than manually typing out the names, especially since it can be set for a complete class.

It is impossible to map from multiple names since these will be used in the validation process.

Properties will be mapped before validation, the mapped property will be removed from the payload and replaced with its original name. This change will also be stored in a MappingMap. This map is required when validation fails since Laravel's validator will output messages for the original property names that failed the validation. Since these names will be the original ones and not the mapped ones, we need the MappingMap to map them back so that these validation messages can appear beneath the correct fields in the UI.

Since the validation errors need to be remapped, we'll also need to create a data-specific extended version of the MessageBag, Validator, and ValidationException since it is not possible to update the names of the properties in the MessageBag.


Full Stack Artisan

A premium Laravel course on
building advanced, reactive user interfaces.

Join us on an excursion through Laravel, View Models, Resources, Laravel Data, TypeScript, Inertia, Vue.js, Tailwind, and more.

Subscribe for updates

We'll only use your email address to send periodical
Full Stack Artisan updates.