Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel 5 Form Request data pre-manipulation

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

like image 720
Jonathon Avatar asked Mar 04 '15 12:03

Jonathon


3 Answers

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();
}
like image 105
Julia Shestakova Avatar answered Oct 13 '22 20:10

Julia Shestakova


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

like image 23
BenSampo Avatar answered Oct 13 '22 18:10

BenSampo


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.

like image 13
Jonathon Avatar answered Oct 13 '22 20:10

Jonathon