Laravel 5 how to validate route parameters?
Asked Answered
V

10

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.

Vigilante answered 14/5, 2015 at 13:11 Comment(4)
I have already see this: how-to-validate-route-parameters-in-laravel-5Vigilante
Do you need to validate only route parameters or "mixed" with request parameters?Patronize
Only route parameters.. There are some difference? Thanks!Vigilante
have you try using middleware. you even can include more that one middleware. so you can use in group routings. please refer here laravel.com/docs/5.2/middlewareSoakage
R
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.

Riffraff answered 31/5, 2016 at 16:3 Comment(3)
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: pastebin.com/pjwrPme6.Buddybuderus
It works on 5.5/5.5 if you add the $all = null parameter to the function.Chinaware
I can't use your solution. I have a question about your answer: #52402639Giulio
P
18

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
Parade answered 28/6, 2016 at 2:3 Comment(0)
B
11

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());
}
Burning answered 7/3, 2017 at 22:20 Comment(2)
If you're using FormRequest you can also override the validationData method like protected function validationData() { return $this->route()->parameters() + $this->all(); }.Mcewen
For L8 I use your shorted version $validator = Validator::make(array_merge($request->all(), $request->route()->parameters()), ['country' => 'required...Hellman
L
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.

Ladykiller answered 2/6, 2016 at 19:8 Comment(1)
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.Mcewen
D
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)
        ];
    }
}
Dagnah answered 10/7, 2017 at 22:22 Comment(5)
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..Mcewen
@Mcewen - how would you recommend applying the trait? Extend something else, or make request class from scratch?Dagnah
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.Mcewen
In most cases it should be fine to use the trait on whatever class is spit out by php artisan make:request ...Dagnah
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(); }?Mcewen
C
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);
}
Contrabandist answered 7/6, 2016 at 9:26 Comment(0)
R
1
$request->merge(['id' => $id]);
...
$this->validate($request, $rules);

or

$request->merge(['param' => $this->route('param')]);
...
$this->validate($request, $rules);
Retrusion answered 20/3, 2019 at 19:19 Comment(0)
P
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']
    ];
Petepetechia answered 7/6, 2016 at 15:2 Comment(0)
I
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();
    }
Intosh answered 25/4, 2019 at 15:40 Comment(0)
P
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
        );
    }
Plummy answered 24/5, 2019 at 8:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.