#1 [Illuminate2] Model Self-Validation

Helium Services
3 min readNov 19, 2020

In my first article a few weeks ago, I covered the benefits of a Model-centric approach to Laravel. If you haven’t already, check out that article here.

This week is the first in a long line of tutorials for the Model-centric components I’ve built over the past several months. If you read my first piece, you’ll know that all of my components were originally developed as the result of mish-mash experimentation and hacking something together until it did what I wanted. As I publish this tutorial series, I’ll also be cleaning things up by re-developing each of the components into my new package, helium/illuminate2.

Model Self-Validation

One of my personal favorite Model-centric components that I’ve developed is a system for Models to self-validate their own attributes. Using the Laravel-provided Validator component, Models can evaluate and enforce their own rules for attribute values just before sending updates to your application’s database.

Why?

Shifting validation logic from the Controller and Request objects to your Model has several benefits.

First, it allows you to simplify your Controller code and remove the need to redundantly declare validation rules on every request endpoint.

Secondly, it provides an accessible method for data validation outside of the context of Controller endpoints. From Tinker sessions and other command-line interfaces to Background Jobs, using Validation on the Model provides a consistent and accessible approach to perform data validation without manually constructing a Validator in each individual context.

Finally, Model self-validation allows you to guarantee your Model’s data structure at the last possible moment before committing to the database. Validation in the Controller provides an inherent risk that additional code can apply changes to your data after it was validated. In turn, validation during the Model’s saving event provides a safeguard against invalid modifications to the data before saving to the database.

Installation

To get started, install the package into your Laravel project:

composer require helium/illuminate2

Please note that this package is only compatible with Laravel 8.14 and later, so you should make sure your project is up-to-date with the most recent release.

Model Validation Rules

Include the ValidatesAttributes trait on your Model. Be sure to use the correct trait, as the Laravel Validation component also uses a trait by the same name internally.

use Helium\Illuminate2\Database\Eloquent\Concerns\ValidatesAttributes; use Illuminate\Database\Eloquent\Model;class User extends Model
{
use ValidatesAttributes;
}

Great! Now all that’s left is to declare your validation rules.

protected $validationRules = [
'first_name' => 'required|string',
'middle_name' => 'nullable|string',
'last_name' => 'required|string',
'age' => 'required|integer',
'team_id' => 'required|exists:teams,id'
];

Running Validation

By default, the ValidatesAttributes trait ensures that validation is run automatically when the Model’s saving event is fired. However, if you want to disable this behavior, simply set the $validatesOnSave property on your Model to false.

protected $validationMessages = [
'first_name.required' => 'The :attribute field cannot be empty!',
'last_name.required' => 'The :attribute field cannot be empty!',
...
];
protected $validationCustomAttributes = [
'first_name' => "\"First Name\"",
'last_name' => "\"Last Name\"",
...
];

For more complex validation rules, such as rules that cannot be declared by string or conditional logic, you can override the getValidationRules function.

public function getValidationRules(): array {
$rules = [
'first_name' => 'required|string',
'middle_name' => 'nullable|string',
'last_name' => 'required|string',
'user_type' => [
'required',
Rule::in(['admin', 'user'])
],
'age' => 'required|integer'
];
if ($this->age >= 18) {
$rules['team_id'] = 'required|exists:teams,id';
}
return $rules;
}

Similarly, you may override the getValidationMessages and getValidationCustomAttributes functions as needed.

Finally, to fully customize the Validator instance, override the getValidator function:

/**
* @return Illuminate\Contracts\Validation\Validator
*/
public function getValidator(): Validator {
return new MyCustomValidator(...);
}

Conclusion

Again, this is the first in a series of tutorials covering the various Model-centric components I’ve developed over the past several months. Check back in in two weeks to see the next tutorial on the GuardsAttributesOnCreate trait.

--

--