Manipulating request data before performing validation in Laravel

When using form request classes, it can be really handy to be able to manipulate the request data before running any of the validation rules. This could be to:

  • Coerce the data into a format the validation is expecting (e.g. convert a list of comma separated values to an array).
  • Cast a value to another type (e.g. string to integer or string to boolean)
  • Account for common user errors, like typos.
  • Remove inappropriate or potentially malicious content from input data.

Example form request

Let's start with an example form request for storing a blog post. Take notice of the rules that are applied the inputs.

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StorePostRequest extends FormRequest
{
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'title' => 'required|max:200',
            'body' => 'required',
            'tags' => 'required|array|max:10',
            'is_published' => 'required|boolean',
            'author_name' => 'required',
        ];
    }
}

Now let's assume that a user has submitted a form to create a post and has passed the following input data:

[
    'title' => 'My bolg post',
    'body' => 'This is the <script>alert('Evil!')</script> post body.',
    'tags' => 'laravel,code,updates',
    'is_published' => 'true',
    'author_name' => 'Ben Sampson',
]

The tags are sent from the front end as a list of comma separated values and the is_published input is interpreted as a string. Both of these will fail validation despite being correct from a content perspective, just in the wrong format.

Additionally, in a terrible spell of bad luck, it looks like the title contains a typo and the body contains some malicious code. While technically it could be said that these aren't validation issues, we can still help to save the user from themselves and protect our application.

The prepareForValidation method

Taking a look at the base Illuminate\Foundation\Http\FormRequest class, which all form requests extend, we can see that it uses a trait called ValidatesWhenResolvedTrait. This trait contains the method we're looking for which allows us to tap into the request data before any validation begins. The method is appropriately named prepareForValidation and has no default actions, meaning it's been put there for the sole purpose of being overridden.

protected function prepareForValidation()
{
    // no default action
}

Manipulating the request data

Within our form request, we can make use of the prepareForValidation method to manipulate any of the request input values.

Because the base FormRequest class extends the Request class we have access to the merge helper method which we can use to update just the input values that we need to. We can also access the input values themselves like properties on the class.

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StorePostRequest extends FormRequest
{
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'title' => 'required|max:200',
            'body' => 'required',
            'tags' => 'required|array|max:10',
            'is_published' => 'required|boolean',
            'author_name' => 'required',
        ];
    }

    /**
     * Prepare the data for validation.
     *
     * @return void
     */
    protected function prepareForValidation()
    {
        $this->merge([
            'title' => fix_typos($this->title),
            'body' => filter_malicious_content($this->body),
            'tags' => convert_comma_separated_values_to_array($this->tags),
            'is_published' => (bool) $this->is_published,
        ]);
    }
}

Using the magic helper methods that exist elsewhere in our application (🤫 they don't), we've managed to manipulate the data such that it now looks like the following before validation is performed:

[
    'title' => 'My blog post',                  // ✅ Typo fixed!
    'body' => 'This is the post body.',         // ✅ Malicious content removed!
    'tags' => ['laravel', 'code', 'updates'],   // ✅ Now an array!
    'is_published' => true,                     // ✅ Now a boolean!
    'author_name' => 'Ben Sampson',             // ✅ Still the same
]

The values are updated on the request itself, so however we access them in our controller after performing validation, we'll be getting the manipulated values back.