Laravel 5 Form Request data pre-manipulation

40,513

Solution 1

in laravel 5.1 you can do that

<?php namespace App\Http\Requests;

class UpdateSettingsRequest extends Request {

public function authorize()
{
    return true;
}

public function rules()
{
    return [];
}

protected function getValidatorInstance()
{
    $data = $this->all();
    $data['date_of_birth'] = 'test';
    $this->getInputSource()->replace($data);

    /*modify data before send to validator*/

    return parent::getValidatorInstance();
}

Solution 2

Since Laravel 5.4, you can use the prepareForValidation method on FormRequest classes.

<?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,
        ]);
    }
}

There's a more detailed write-up here: https://sampo.co.uk/blog/manipulating-request-data-before-performing-validation-in-laravel

Solution 3

Note: This question and answer were both posted before Laravel 5.1 was released and both target 5.0. For 5.1 and above, see this answer by @Julia Shestakova or this answer by @BenSampo for a more modern solution.


After some messing around myself I've come up with the following:

app/Http/Requests/Request.php

<?php namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

abstract class Request extends FormRequest {

    /**
    * Override the initialize method called from the constructor to give subclasses
    * an opportunity to modify the input before anything happens.
    *
    * @param array $query
    * @param array $request
    * @param array $attributes
    * @param array $cookies
    * @param array $files
    * @param array $server
    * @param null $content
    */
    public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null)
    {
        parent::initialize($query, $request, $attributes, $cookies, $files, $server, $content);

        // Grab the input
        $data = $this->getInputSource()->all();
        // Pass it off to modifyInput function
        $data = $this->modifyInput($data);
        // Replace modified data back into input.
        $this->getInputSource()->replace($data);
    }


    /**
    * Function that can be overridden to manipulate the input data before anything
    * happens with it.
    *
    * @param array $data The original data.
    * @return array The new modified data.
    */
    public function modifyInput(array $data)
    {
        return $data;
    }

}

Then on extending classes you can just override the modifyInput method like this:

app/Http/Requests/TestRequest.php

<?php namespace App\Http\Requests;

class TestRequest extends Request {

    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [];
    }

    /**
     * Modify the input.
     */
    public function modifyInput(array $data)
    {
        $data['date_of_birth'] = 'something';

        // Make sure to return it.
        return $data;
    }

}

This seems to work for my needs. I'm not sure of any drawbacks of doing it this way so I welcome any comments/criticism.

The answer given by The Shift Exchange above will also work just fine.

Solution 4

I took a similar approach to Julia Logvina but I think this way is a slightly more elegant way to add/modify fields before validation (Laravel 5.1)

<?php

namespace App\Http\Requests;

use App\Http\Requests\Request;

class UpdateSettingsRequest extends Request
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {        
        return [];
    }


    /** 
     * Extend the default getValidatorInstance method
     * so fields can be modified or added before validation
     *
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function getValidatorInstance()
    {

        // Add new data field before it gets sent to the validator
        $this->merge(array('date_of_birth' => 'test'));

        // OR: Replace ALL data fields before they're sent to the validator
        // $this->replace(array('date_of_birth' => 'test'));

        // Fire the parent getValidatorInstance method
        return parent::getValidatorInstance();

    }

}

This will extend the default getValidatorInstance so we can modify the input values in the request before it gets to the validator (preventing it from using the original unmodified data). Once the data has been modified, it fires off the original getValidatorInstance then everything continues as normal.

You can either use $this->replace(array()) or $this->merge(array()) your new fields into your request. I've included an example of how to do both in the snippet above.

replace() will replace all fields with the array you provide.

merge() will add a new field into your request.

Solution 5

I think this is the best approach to do so: Laravel 5.1 Modify input before form request validation

In Laravel 5.4+ there is a dedicated method for stuff like this so use it: prepareForValidation

Share:
40,513
Jonathon
Author by

Jonathon

Updated on February 13, 2020

Comments

  • Jonathon
    Jonathon over 4 years

    I'm processing a form where a user can update their date of birth. The form gives the user 3 separate fields for day, month and year. On the server-side of course I want to treat these 3 separate fields as one value i.e. yyyy-mm-dd.

    So before validation and updating my database, I want to alter the form request to create a date_of_birth field by concatenating year, month and day with - characters to create the date format I need (And possibly unset the original 3 fields).

    Achieving this manually with my controller is not a problem. I can simply grab the input, join the fields together separated by - characters and unset them. I can then validate manually before passing off to a command to deal with the processing.

    However, I would prefer to use a FormRequest to deal with the validation and have that injected into my controller method. Therefore I need a way of actually modifying the form request before validation is executed.

    I did find the following question which is similar: Laravel 5 Request - altering data

    It suggests overriding the all method on the form request to contain the logic for manipulating the data prior to validation.

    <?php namespace App\Http\Requests;
    
    class UpdateSettingsRequest extends Request {
    
        public function authorize()
        {
            return true;
        }
    
        public function rules()
        {
            return [];
        }
    
        public function all()
        {
            $data = parent::all();
            $data['date_of_birth'] = 'test';
            return $data;
        }
    

    This is all well and good for the validation, but overriding the all method doesn't actually modify the data on the form request object. So when it comes to executing the command, the form request contains the original unmodified data. Unless I use the now overridden all method to pull the data out.

    I'm looking for a more concrete way to modify the data within my form request that doesn't require the calling of a specific method.

    Cheers

  • Jonathon
    Jonathon over 9 years
    Thanks for your reply. Is there not a cleaner, "one time" way of doing it? It feels very dirty the data must be manipulated every time data is requested. From looking into the code it seems that the all() function is called every time data is requested (i.e. from input, except etc.).
  • Laurence
    Laurence over 9 years
    Its a very clean way - because it is neatly handled inside the FormRequest object. This means the controller and other aspects of your application have no idea what is occurring. The fact it occurs each time all() is called is a non-issue - the optimisation level you are talking about is trival. Focus on writing good clean maintainable code will have a better outcome :)
  • Jonathon
    Jonathon over 9 years
    Yeah it's definitely trivial in this scenario. But in theory, if there was a lot to do here, it could be a different story. I prefer to write optimal code where I can. I would liken this approach to getting the length of an array we are looping through on every iteration. It works, the optimisation is probably trivial but it all mounts up. I've tried looking for other methods to override and have came to the conclusion that the initialize method found in Symfony\Component\HttpFoundation\Request might be the way to go. Any thoughts on this? It's called from the constructor.
  • Laurence
    Laurence over 9 years
    The thing is - the all() method is already doing an array loop on every iteration anyway. If you look at the original all() method - this is it: return array_replace_recursive($this->input(), $this->files->all()). So you making this modification is not having any impact on your performance. You are trying to optimizie a process that doesnt need optimizing...
  • MisterBla
    MisterBla almost 9 years
    +1 This is actually a very nice way of doing it, I like it. Although I'd cache the sourceInput by means of $source = $this->getInputSource();
  • Jonathon
    Jonathon over 7 years
    Sure, in 5.4 this is no doubt the best solution to the problem. However, for versions prior to 5.4 (This question was asked in March 2015 where 5.0 was the version at that time), then the other answers given below are more appropriate, such as the one given by @Julia Logvina for 5.1, which is a long term support (LTS) version
  • Thomas Venturini
    Thomas Venturini over 7 years
    Jep, but folks like me are still finding this if they look for the gerneral topic on 5.x so I think it is valid to mention the solution for 5.4+ ;)
  • Steve Bauman
    Steve Bauman almost 7 years
    Thanks @Thomas Venturini! Using 5.5 so definitely helped. +1
  • Jonathon
    Jonathon almost 6 years
    Although at the time I asked this question (March 2015), Laravel 5.1 was not released (It was released in June 2015), I will accept this answer. For Laravel 5.0, see some of the other answers. Thanks, Julia!
  • CodeConnoisseur
    CodeConnoisseur about 5 years
    Is there a way to change where the FormRequest redirects to or does it only redirect back when there are errors?
  • CodeConnoisseur
    CodeConnoisseur about 5 years
    Is there a way to change where the FormRequest redirects to or does it only redirect back when there are errors?
  • Ratto
    Ratto over 3 years
    Why is this method not documented until version 6.x? :-/