“Two books on a desk near a MacBook with lines of code on its screen” by Émile Perron on Unsplash

Often in Laravel we’ll need to create a custom validation rule. There’s been a few different ways over different versions of Laravel and it’s something I often find developers have struggled with. Those developers require something more complex than the baked in validation rules but don’t know the best way to add them, sometimes opting to just make a regex rule. This is my personal favourite way that I’ve been doing it in Laravel 5.6.

For this tip I’m just going to cover the most basic and universal validation rule that I could think of, validating that a number is even.

Let’s write a test

Another thing I see in a lot of developers is a misunderstanding when and how to test, there’s no easy answer. In this situation though, a validation rule is the perfect use case. We only care about the class and not how it fits with every use case of it. Our goal is simple, that it passes the right values and fails the wrong ones, nothing more and nothing less.

First we’ll whip up a unit test using the artisan command php artisan make:test -u Rule\\EvenNumbersTest which will create the file tests/Unit/Rule/EvenNumberRuleTest.php .

The next step is creating a test. It’ll look like the following which I’ll explain shortly.

What this test class will do is execute the testEvenNumbersPass() method for each number provided by the evenNumbers() with the expectation our Rule will determine that each number is an even. The same is done with the odd numbers except we expect the result to be false. We do this because it’s a nice way to lay it out but also by using a data provider when one of the number fails PHPUnit will actually tell us which number failed.

I will be honest at this point now, I have no clue if 2.1 should be an even number or even if -2.1 should be even, if I used this rule I’d likely make sure it was an integer using the int rule, the point is more to look at the boundaries of what values should be considered even and which should be odd. The setup method is purely to create an instance of our new EvenNumberRule which we will make when implementing.

Now we can run vendor/bin/phpunit (or just phpunit if you have it installed globally already) and we’ll have our tests break because there’s no implementation yet.

Making our Rule

This will actually be pretty quick. First we can make the rule through an artisan command like most things in Laravel with php artisan make:rule EvenNumberRule . Then we can edit our rule created in app/Rules/EvenNumberRule.php as follows.

As you can see from the code we’ve added the body for the passes($attribute, $value) method to check the value if it’s even it will return true and odd it will return false . We’ve also edited the message() method to return an appropriate message that the user will receive.

We then rerun the tests with PHPUnit and that’s it, we should have instantly passing tests for our new validation rule.

Using our validation through the Rule class

This is where I’m going to get a tiny bit opinionated. Laravel 5.4 introduced the Illuminate\Validation\Rule static methods, for example if you want to have a ‘In’ rule but using the values of an array you can simply call Rule::in($arrayOfValues) and it sets up the rule rather than having to explode a and concatenate the rule which in turn looks messy and might not get parsed correctly if the array is of strings and one such string has a comma.

What we would typically do is make a validation array like so, with a new instance of EvenNumberRule included the rules for a field.

The neat thing about the Rule class though it applies the Laravel macro trait. So now we can make our own even() method which will return an instance of EvenNumberRule .

You might ask why we should do this? Well it’s a nice structure in my opinion, all of our validation rules are coming from the same class that creates them. This also stops us having to add lots of class imports to the top of our files when we want to use lots of them to validate some input.

We can then even add a small little method to our earlier created test to check that the macro is being applied.

If you want to play around with the code directly then you can clone it from GitHub.