42

I want to validate the route parameters in the "form request" but don't know how to do it.

Below is the code sample, I am trying with:

Route

// controller Server
Route::group(['prefix' => 'server'], function(){
    Route::get('checkToken/{token}',['as'=>'checkKey','uses'=> 'ServerController@checkToken']);
});

Controller

namespace App\Http\Controllers;


use App\Http\Controllers\Controller;

use Illuminate\Http\Request;
use App\Http\Requests;


class ServerController extends Controller {
    public function checkToken( \App\Http\Requests\CheckTokenServerRequest $request) // OT: - why I have to set full path to work??
        {   
            $token = Token::where('token', '=', $request->token)->first();      
            $dt = new DateTime; 
            $token->executed_at = $dt->format('m-d-y H:i:s');
            $token->save();

            return response()->json(json_decode($token->json),200);
        }
}

CheckTokenServerRequest

namespace App\Http\Requests;

use App\Http\Requests\Request;

class CheckTokenServerRequest extends Request {

        //autorization

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

            return [
                'token' => ['required','exists:Tokens,token,executed_at,null']
            ];
        }

}

But when I try to validate a simple url http://myurl/server/checkToken/222, I am getting the response: no " token " parameter set.

Is it possible to validate the parameters in a separate "Form request", Or I have to do all in a controller?

ps. Sorry for my bad English.

Majid Alaeinia
  • 962
  • 2
  • 11
  • 27
jbernavaprah
  • 593
  • 1
  • 5
  • 11

10 Answers10

61

For Laravel < 5.5:
The way for this is overriding all() method for CheckTokenServerRequest like so:

public function all() 
{
   $data = parent::all();
   $data['token'] = $this->route('token');
   return $data;
}

EDIT
For Laravel >= 5.5:
Above solution works in Laravel < 5.5. If you want to use it in Laravel 5.5 or above, you should use:

public function all($keys = null) 
{
   $data = parent::all($keys);
   $data['token'] = $this->route('token');
   return $data;
}

instead.

Majid Alaeinia
  • 962
  • 2
  • 11
  • 27
Marcin Nabiałek
  • 109,655
  • 42
  • 258
  • 291
  • 1
    I agree that this is the only one solution for this problem, if You want to stick to validation inside request class. Small improvement would be to replace all route parameters automatically: http://pastebin.com/pjwrPme6. – Giedrius Kiršys Jun 01 '16 at 04:53
  • 1
    It works on 5.5/5.5 if you add the `$all = null` parameter to the function. – cweiske Jun 01 '18 at 13:09
  • 1
    I can't use your solution. I have a question about your answer: https://stackoverflow.com/questions/52402139/how-to-override-all-method-in-request-validation-class – Ali Hesari Sep 19 '18 at 10:00
17

Override the all() function on the Request object to automatically apply validation rules to the URL parameters

class SetEmailRequest
{

    public function rules()
    {
        return [
            'email'    => 'required|email|max:40',
            'id'       => 'required|integer', // << url parameter
        ];
    }

    public function all()
    {
        $data = parent::all();
        $data['id'] = $this->route('id');

        return $data;
    }

    public function authorize()
    {
        return true;
    }
}

Access the data normally from the controller like this, after injecting the request:

$setEmailRequest->email // request data
$setEmailRequest->id, // url data
Mahmoud Zalt
  • 30,478
  • 7
  • 87
  • 83
10

If you dont want to specify each route param and just put all route params you can override like this:

Laravel < 5.5:

public function all()
{
   return array_merge(parent::all(), $this->route()->parameters());
}

Laravel 5.5 or above:

public function all($keys = null)
{
   // Add route parameters to validation data
   return array_merge(parent::all(), $this->route()->parameters());
}
William Desportes
  • 1,412
  • 1
  • 22
  • 31
mk_
  • 179
  • 1
  • 4
  • 1
    If you're using `FormRequest` you can also override the `validationData` method like `protected function validationData() { return $this->route()->parameters() + $this->all(); }`. – giovannipds Oct 25 '18 at 23:22
  • For L8 I use your shorted version $validator = Validator::make(array_merge($request->all(), $request->route()->parameters()), ['country' => 'required... – Vit Oct 27 '21 at 18:43
5

The form request validators are used for validating HTML form data that are sent to server via POST method. It is better that you do not use them for validating route parameters. route parameters are mostly used for retrieving data from data base so in order to ensure that your token route parameter is correct change this line of your code, from

$token = Token::where('token', '=', $request->token)->first();

to

$token = Token::where('token', '=', $request->input(token))->firstOrFail();

firstOrFail() is a very good function, it sends 404 to your user, if the user insert any invalid token.

you get no " token " parameter set because Laravel assumes that your "token" parameter is a POST data which in your case it is not.

if you insist on validating your "token" parameter, by form request validators you gonna slow down your application, because you perform two queries to your db, one in here

$token = Token::where('token', '=', $request->token)->first();

and one in here

return [
            'token' => ['required','exists:Tokens,token,executed_at,null']
        ];

I suggest to use firsOrFail to do both validating and retrieving at once.

Salar
  • 5,305
  • 7
  • 50
  • 78
  • I like your concern and I do agree that parameters validation is not a real responsibility of a `FormRequest` but isn't that so much useful? There's nothing saying at Laravel docs that FormRequests should just be used for `POST` requests. I mean, we have `DELETE`, `PUT` and other types of request that need data validation. If a FormRequest is the way to separate Controller logic from Validation logic, that's the way to go. What do you think about it? I think I changed my mind from this point of view. – giovannipds Oct 25 '18 at 23:29
3

A trait can cause this validation to be relatively automagic.

Trait

<?php

namespace App\Http\Requests;

/**
 * Class RouteParameterValidation
 * @package App\Http\Requests
 */
trait RouteParameterValidation{

    /**
     * @var bool
     */
    private $captured_route_vars = false;

    /**
     * @return mixed
     */
    public function all(){
        return $this->capture_route_vars(parent::all());
    }

    /**
     * @param $inputs
     *
     * @return mixed
     */
    private function capture_route_vars($inputs){
        if($this->captured_route_vars){
            return $inputs;
        }

        $inputs += $this->route()->parameters();
        $inputs = self::numbers($inputs);

        $this->replace($inputs);
        $this->captured_route_vars = true;

        return $inputs;
    }

    /**
     * @param $inputs
     *
     * @return mixed
     */
    private static function numbers($inputs){
        foreach($inputs as $k => $input){
            if(is_numeric($input) and !is_infinite($inputs[$k] * 1)){
                $inputs[$k] *= 1;
            }
        }

        return $inputs;
    }

}

Usage

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class MyCustomRequest extends FormRequest{
    use RouteParameterValidation;

    /**
     * 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 [
            //
            'any_route_param' => 'required'//any rule(s) or custom rule(s)
        ];
    }
}
Tarek Adam
  • 3,387
  • 3
  • 27
  • 52
  • Personally, I don't think anybody should extend FormRequest for that, it won't be a real form request, if you know what I mean. The trait idea is still great, though.. – giovannipds Aug 16 '18 at 17:32
  • @giovannipds - how would you recommend applying the trait? Extend something else, or make request class from scratch? – Tarek Adam Aug 21 '18 at 22:33
  • I didn't think about it, perhaps just extending the basic `Request` is the way to go, not the trait. Just not mixing it with `FormRequest`. Of course you can base your code at `FormRequest` but should not extend it from there. As I said, they're things for different concerns. If sometime later `FormRequest` change for any reason, it won't impact on your own `Request`. And of course, that's just my opinion. – giovannipds Aug 22 '18 at 17:53
  • In most cases it should be fine to use the trait on whatever class is spit out by `php artisan make:request ...` – Tarek Adam Aug 23 '18 at 18:20
  • Yeah... I still thinking about this damn thing. I hate the idea of extending the original `FormRequest` but it's just too damn useful. What does your trait differs from just overriding the `validationData` method like this: `protected function validationData() { return $this->route()->parameters() + $this->all(); }`? – giovannipds Oct 25 '18 at 23:20
1

For \App\Http\Requests\CheckTokenServerRequest you can add use App\Http\Requests\CheckTokenServerRequest; at the top.
If you pass the token by url you can use it likes a variable in controller.

public function checkToken($token) //same with the name in url
{

    $_token = Token::where('token', '=', $token)->first();      
    $dt = new DateTime; 
    $_token->executed_at = $dt->format('m-d-y H:i:s');
    $_token->save();

    return response()->json(json_decode($token->json),200);
}
Felix
  • 571
  • 14
  • 34
1
$request->merge(['id' => $id]);
...
$this->validate($request, $rules);

or

$request->merge(['param' => $this->route('param')]);
...
$this->validate($request, $rules);
Rene Berwanger
  • 157
  • 1
  • 2
0

You just missing the underscore before token. Replace with

_token

wherever you check it against the form generated by laravel.

public function rules()
{

    return [
        '_token' => ['required','exists:Tokens,token,executed_at,null']
    ];
Cristo
  • 700
  • 1
  • 8
  • 20
0

FormRequest has a method validationData() that defines what data to use for validation. So just override that one with route parameters in your form request class:

    /**
     * Use route parameters for validation
     * @return array
     */
    protected function validationData()
    {
        return $this->route()->parameters();
    }
Mladen Janjetovic
  • 13,844
  • 8
  • 72
  • 82
0

or leave most of the all logic in place and override input method from trait \Illuminate\Http\Concerns\InteractsWithInput

     /**
     * Retrieve an input item from the request.
     *
     * @param string|null $key
     * @param string|array|null $default
     * @return string|array|null
     */
    public function input($key = null, $default = null)
    {
        return data_get(
            $this->getInputSource()->all() + $this->query->all() + $this->route()->parameters(), $key, $default
        );
    }
Paul Rysevets
  • 39
  • 1
  • 4