How to use custom Laravel ValidationRule rules in Statamic

Published March 5th, 2024

Statamic has great support for Laravel’s baked-in validation rules – like required, min, max and so on, and even support for Illuminate\Contracts\Validation\Rule rules. However, with Laravel 10, this Rule contract has been marked as deprecated, and to use Illuminate\Contracts\Validation\ValidationRule instead.

However, Statamic did not have support for the ValidationRule approach – and the previous Rule approach was a little limiting.

So I got tinkering with a little PR to add this to Statamic, and as of Statamic 4.51.0, you’re able to use your ValidationRule rules within Statamic.

Given the differences between Rule and ValidationRule, a slightly different syntax would be needed to offer the same level of functionality: and my original PR had a bit of a clunky syntax for hooking everything in to a ValidationRule. Thankfully the awesome Jason at Statamic reworked by weird syntax in to one that is much more fluent, intuitive and elegant.

With a ValidationRule, you’ll have a single validate method where you’ll receive:

  • the field’s handle as $attribute,

  • the field’s value as $value, and

  • the $fail callback

You can perform whatever validation checks you need, and if validation fails, call the $fail closure, and pass your validation message – which can even support translations which is great for internationalisation support for addon development.

3namespace App\Rules;
5use Closure;
6use Illuminate\Contracts\Validation\ValidationRule;
8class MyValidationRule implements ValidationRule
10 public function validate(string $attribute, mixed $value, Closure $fail): void
11 {
12 // do something to give yourself validation
13 $failedValidation = true; // let's just hard wire this
15 if ($failedValidation) {
16 $fail('The message to appear in the CP.');
17 }
18 }

Using a ValidationRule in Statamic is as simple as adding them to the Rules input like you would any other rule within the Statamic CP.

Adding the MyValidationRule to the Rules configuration within the Statamic CP.

The message passed to $fail is what will appear within the Statamic CP as a validation message.

An example of the validation rule showing the $fail message within Statamic's CP for the "Title" field.

It is also possible to pass input parameters to your ValidationRule. This can be really helpful if your rule has a configurable parameter used for testing. Let’s say you have a rule that needs to calculate whether your field (such as a Grid or Replicator) has values that add up to a specific number – like a number of rows that need to add up to 100.

You could write a rule that does this – but you could also write a more generic rule that allows the exact sum to be configurable - maybe you need an exact sum of 100 somewhere, but only 6 somewhere else. This approach means you can write one rule that can be used in both places. Here’s a quick example:

3namespace App\Rules;
5use Closure;
6use Illuminate\Contracts\Validation\ValidationRule;
8class ExactSum implements ValidationRule
10 public function __construct(protected $exactSum = 50)
11 {
12 }
14 public function validate(string $attribute, mixed $value, Closure $fail): void
15 {
16 $sum = 0;
18 // do whatever you need with $value to perform your validation
20 if ($sum != $this->exactSum) {
21 $fail('Your values must add up to '.$this->exactSum.'.');
22 }
23 }

You can then pass this $exactSum with your Rule configuration within Statamic:

Passing a custom "sum" to the ExactSum rule within the Statamic CP.

And if you’re wanting to use named parameters, those will work too:

Configuration with named params in a ValidationRule via the Statamic CP

And of course, if you’re a YAML fan, you can also edit your Blueprint by hand – just remember to escape your quotes if using parameters.

2 - 'new App\Rules\ExactSum(exactSum: 6)'

It is great to see Statamic up and running with Laravel’s ValidationRule approach – and can help ensure new projects aren’t using the deprecated Illuminate\Contracts\Validation\Rule too.

Hero image credit: Markus Winkler (Source)

You may be interested in...