How to validate Eloquent Models with dynamic patterns and multipe database connections

Loving Laravel and Eloquent Models

Well after some years sticking with Zend Framework, we at me’kono completely fell for Laravel as our main PHP Web development framework, which has a beautiful and easy to read MVC design pattern. One of the most frequently used Classes / Feature of Laravel is the Eloquent Class which – basically speaking – represents an entry in your database and adds methods and relations to it. One very important method is the save()  function obviously, which gathers all the data of the current object instance and tries to save it to the database.

Validation before saving

Since we didn’t want this method to simply run against the database and maybe return an error, the decision was made to have a validation before even starting the storage process. To make things more readable, the validation patterns are stored in a static array in the notation defined by the Laravel Validation Class:

To have these validation rules being applied each time the object gets stored to the database, simply override the save()  method with something like this:

And the isValid()  method we use comes here:

As you can see the method gets all attributes of the object, dynamically creates a Validator from the class’s validation rules and applies them. If validation fails the errors get stored in the object’s errors property. Hence you can better hunt down why storing the data ran into trouble.

Some special DB fields

Ok, this is pretty easy and straight forward, but occasionally you might run into the following problem:
Let’s pick up the common requirement that there should be no duplicate email address in your Users table, i.e. email should be unique. So you can change the validation rule to the following:

If you try this, it works fine if your user’s data is inserted once and then only read from the database, meaning you cannot update it. The eloquent update()  method also uses the save()  method. But since your email is already in the database, the logic just implemented prevents you from storing to the database.

But the guys from Laravel are clever and have taken care of this. You may specify an additional parameter for an ID, which should be ignored or is fine the have the same email (from the docs):

Make it dynamic

Almost there!

Just some additional thoughts: This way all your isValid()  methods have to be custom made for each model. And – you have to distinguish between save operations and update operations. So we came up with the idea of dynamic validation rules. The strings defining the validation are parsed before creation of the Validator and some variables of the object are filled in upon request (changes to above highlighted):

The syntax is dead simple: %[name of object property]$[printf output format]

The array_walk function  iterates over the validation rules and nvsprintf  applies the object properties to the validation strings.

Multiple DB connections and upgrading to Laravel 5.2

When we starting with Laravel, the framework stood at version 4, then we quickly moved to 5.1 – which is the first Long Term Support (LTS) Version of the framework and our project grew bigger and bigger. Now in some parts the framework is using two database connections – even in it model validation. So to search in the correct database for unique values, you have to define, which connection to use. In Laravel 5.1 this may look like this:

We created the Eloquent object over a specific database connection ( User::on('my-other-db')->find($user_public_id); ) and before updating it to the database, the Validator has to be assigned the same database connection.

Now in Laravel 5.2 the validateUnique() method of the Validator Class changed reading the database connection from the defining string. So the database connection has to be set in the validation rules. Since we already had implemented dynamic validation rules this was easy to achieve:

And a slight change to our isValid() method:

 

Leave a Reply

Your email address will not be published. Required fields are marked *

*

*