Adding custom validation rules to Statamic 3

Laravel's Rule functionality are incredibly powerful. Learn how to make use of them in Statamic 3.

Published December 7th 2022

Required knowledge

How to set up a form in Statamic, including how to set validation via the Validation tab.

Required code

You will to have a form on your site that has a name field, outputs error messages, and the ability to submit the form.


Why would we need a custom rule?

It’s not unusual when building websites, or web applications, that have forms to control the type of data that can be submitted by a user. To do this in Laravel, we can make use of the many validation rules that the PHP framework provides out of the box.

However, there are times where the available validation rules won’t cover the use case you have. A very common use case that isn’t covered by the default validation rules (and quite frankly, that it isn’t part of the framework in 2022 despite it being something almost every project needs is astounding) is validating names. At first glance, it would seem the default alpha rule would be suitable for this situation. However, as anyone who has tried to use this rule will know, it does not allow spaces. That means if you are capturing full names (or potentially given names if you’re capturing family names separately), the alpha rule is useless as it will prevent users from entering valid data.

So what are you supposed to do? Well, you could fallback to the pattern rule, and manually add regex rules that will achieve what you want to do. This isn’t ideal though, as it requires you to manually add the pattern to every field you need, and runs the risk of those who don’t understand regex accidentally breaking the validation. Wouldn’t it be much better to have a rule that allows you to validate that the input is alpha, with all the common bits a name might have?

What does our rule have to allow?

In order to support all kinds of names, we need allow:

  • • Dashes, which are used in double-barrel names
  • • Apostrophes, common in Celtic name, such as O’Connor.
  • • Diacritics, such as the cedilla which appears on the c in Francois – i.e. François
  • • Spaces

Adding new validation rules to Statamic

Now that we know what we need to allow, we can start looking at how we can build our validation rule so we can use it in Statamic.

Statamic 3 is now built as a package of Laravel, which makes it far easier to leverage the power of the framework; at least compared to Statamic 2, which was built on top of Laravel and locked on version 5.1. That means we can use custom validation rules functionality.

We can easily create the framework a new rule by using Laravel’s CLI tool Artisan. For the purposes of what we’re doing, let’s call our new rule Name. Open a terminal window in the root of your Statamic install and type php artisan make:rule Name.

This will create a new rule file in app/Rules called Name.php. Open it in your code editor. You’ll see a class, with three methods. A constructor, passes(), and message(). Feel free to delete the constructor. We won’t need it for this rule. This should leave code that looks like this:

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class Name implements Rule
{
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'The validation error message.';
    }
}

The first thing we want to do is write the logic for what will pass validation.

We want our rule to pass true or false, and nothing else. This lets us know if it passed, or not. So, inside the passes method, let’s add the line following:

return (bool) preg_match($pattern, $value);

Your code should now look something like:

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class Name implements Rule
{
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        return (bool) preg_match($pattern, $value);
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'The validation error message.';
    }
}

Just to break that down, the (bool) forces the result that is returned to be a boolean, rather than a string, which could result in false positives. Next, preg_match is a PHP function that calls a regular expression ($pattern), and checks if what was submitted by the user ($value) passes or not.

If you check the passes method, you can see we’re passing in $value, but not $pattern. So we need to create that variable.

Above the return, add a new line. We’ll define a new variable, $pattern and set it to null. Your code should now look like:

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class Name implements Rule
{
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        $pattern = null;

        return (bool) preg_match($pattern, $value);
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'The validation error message.';
    }
}

Now, obviously this won’t do anything. So we need a regular expression to assess against. For this Rule, we’re going to use /^[\w{L}\-\s\']+$/. I’m not going to go into this expression as regex is way beyond the scope of this tutorial. However, it does allow dashes, apostrophes, diacritics, and spaces, but rejects anything that contains a character isn’t a letter, or other non-name punctuation. Replace null with the regex.

You code should now look like this:

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class Name implements Rule
{
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        $pattern = '/^[\w{L}\-\s\']+$/';

        return (bool) preg_match($pattern, $value);
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'The validation error message.';
    }
}

The next thing that we need to do is to update the validation message. This is relatively simple. We just need to return a simple, but clear message that makes it clear to people what is wrong. Let’s go with “You can only use letters, spaces, and other common punctuation found in names like dashes and apostrophes”.

However, before you replace the string, while this would be okay for a Laravel-only application, because Statamic is designed with localisation in mind, we need to define a translation key. For most rules, this is going to be validation/name_of_rule. For us, that means it will be `validation/name`. Replace The validation error message with __(validation.name). The __() just tells Laravel to look for a translation key that matches this key.

Your code should now look like:

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class Name implements Rule
{
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        $pattern = '/^[\w{L}\-\s\']+$/';

        return (bool) preg_match($pattern, $value);
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return '__(validation.name).';
    }
}

The next step is to create that key. To do this, go to your resources folder, and create a new folder call lang if it does exist. Inside that folder, create another folder. This folder needs to be the main locale your site is created in. For English, this will be en. For French, this will be fr and so on. Once this folder is created, make a new file called validation.php. Inside the file, add the following code:

<?php

return [

];

As you’ll have noticed, this file is called validation. That matches to the first part of our translation key before the .. Inside the array, we’ll define the second part of the key, and add the error message we defined before. It should look something. Like this:

<?php

return [
    'name' => 'You can only use letters, spaces, and other common punctuation found in names like dashes and apostrophes.'
];

Save both files.

Making the rule available to Statamic

Before we can use our new rule in Statamic, we need to register it so that Statamic can call it (this step isn’t necessary in Laravel as you can new up classes like your Rules). To do this, go to you app/Providers/AppServiceProvider.php.

Inside the boot method, under the commented out Statamic lines, add the following:

Validator::extend('name', function ($attribute, $value, $parameters, $validator) {
    return (new Name())->passes($attribute, $value);
});

You also need to import the classes. These are:

Validator: use Illuminate\Support\Facades\Validator;

Name: use App\Rules\Name;

You can add beneath the other use statements at the top of the file, or import them using your code editor.
If this is the only change you’ve made to this file, it should look similar to this:

App/Providers/ServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
use App\Rules\Name;
use Statamic\Statamic;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        // Statamic::script('app', 'cp');
        // Statamic::style('app', 'cp');

        Validator::extend('name', function ($attribute, $value, $parameters, $validator) {
            return (new Name())->passes($attribute, $value);
        });
    }
}

Applying the new rule

You can now go to the blueprint for the form you want to apply the rule for. Go into the input, and then the validation tab. Type in name (note, the autosuggestion won’t show anything, don’t worry).

When you've added the new rule, it should look like this. When you've added the new rule, it should look like this.

Once you’ve saved your form, you can now do to the front end of your site. Try to input some invalid data like $£@%@ and you should see an error (assuming you’re outputting them) that says “You can only use letters, spaces, and other common punctuation found in names like dashes and apostrophes”.

Change that invalid input to something valid like Joe Bloggs, and hit submit. It should now submit the form, and you’ll be able to see your submission.