strmizo's avatar

How to validate Route Parameters in Laravel 5?

As you know Laravel 5 changes the way you call the validator, the old way is calling the validator facade, but now there is the ValidatesRequests trait in base Controller class, but the validate method accepts the request as the values array, but when you define your route parameters, these values are not stored in Request, so how can I validate those parameters ?

Route:

Route::get('/react-api/{username}', 'ProfileController@getUsername');

Controller:

public function getUsername(Request $request, $username)
{
     $v = $this->validate($request, ['username' => 'required']);
}
$request->all() returns empty array !! but the $username has the value !!

So, the question how can i validate this username parameter ?

0 likes
15 replies
enginerd's avatar

You do need to validate especially if you want your API to have a robust and consistent validation mechanism. If you have a route that needs numeric path parameter and a user passes an alphabetic character instead, it should specifically fail because of that.

I can't figure out how to do this. In my request validation class, I tried putting 'id' (/users/{id}) as a required and it never sees the parameter. I've seen a lot of posts about the same thing and the workaround has been to call the validator explicitly, but that seems like a hack because the type-hinted automatic call in the method definition is much more clean.

3 likes
peterstarling's avatar

that's good enough for a simple regex, what about the following rule 'exists:users,user_id'? Is there a way to include route parameters in the validation?

moak's avatar

I solved this in my custom request by adding the parameters to the all() method


    /**
     * @return array
     */
    public function rules()
    {
        return [
            'slug' => 'required|exists:articles,slug',
        ];
    }

    /**
     * Add parameters to be validated
     * 
     * @return array
     */
    public function all()
    {
        return array_replace_recursive(
            parent::all(),
            $this->route()->parameters()
        );
    }
7 likes
martinbean's avatar

@strmizo I think you’re a little confused about route parameters and request data.

Request data is values POSTed via a form, or contained in the query string. Route parameters are segments that are part of the URL itself. If you use route–model binding, you don’t need to validate them, as Laravel will either find a model instance or throw a 404 Not Found exception.

To manually bind a route parameter to a model, you can do this in the book() method of your RouteServiceProvider class:

$router->bind('username', function ($username) {
    return User::whereUsername($username)->firstOrFail();
});

If you have a route pattern like /react-api/{username}, then this will try and find a user with the specified username. Otherwise, as I say, a 404 exception will be raised.

6 likes
EmilMoe's avatar

Yes throw an error that the username wasn't found, etc

garychen's avatar

Hi, All Validator Route Parameters

Route::post('/category/{category_type}/{id}', 'ApiCategoriesController@storeCategory');
 public function storeCategoryByEnterprise(Request $request, $category_type='', $id='') {
        $validator = Validator::make(
          [
            'id' => $id,
            'category_type' => $category_type,
          ],
          [
            'id' => ['required'],
            'category_type' => ['required', 'in:package,product'
          ],
      ], [
          'id.required' => 'category id required',
          'category_type.required' => 'category type required',
          'category_type.in' => 'category type need package or product',
      ]);
1 like
torkil's avatar

A nice solution for validating route segments is to…

  1. add the segment(s) to the validation data and
  2. use normal request validation as you would with POST or PUT/PATCH

Lets say your route was this: GET /foobar/{some_variable}

In your ShowFoobarRequest class:

public function rules()
{
    return [
        'some_variable' => 'required|string',
    ];
}

protected function validationData()
{
    return array_merge($this->request->all(), [
        'some_variable' => Route::input('some_variable'),
    ]);
}

All you would need to do now was to typehint the request class in your controller's show method, like so:

public function show(ShowFoobarRequest $request, $some_variable) 
{
14 likes
martinbean's avatar

Route parameters should not be getting “validated” in controller actions. An action shouldn’t be invoked if a route parameter is “invalid”.

If you need to check a slug or that a route parameter actually resolves to a model instance, use route–model binding:

Route::bind('article_slug', function ($slug) {
    return Article::published()->whereSlug($slug)->firstOrFail();
});
Route::get('articles/{article_slug}', 'ArticleController@show');

The corresponding route will only be invoked if a published article is found matching the supplied slug. It also means the matching Article instance will be passed to your controller action, where you don’t need to validate it:

class ArticleController extends Controller
{
    public function show(Article $article)
    {
        return view('article.show', compact('article'));
    }
}
2 likes
MladenJanjetovic's avatar

@MOAK - I coulnd't get it to work with all() method, but I dig out the validationData() and override in FromRequest did the trick:

    /**
     * Use route parameters for validation
     * @return array
     */
    protected function validationData()
    {
        return $this->route()->parameters();
    }
1 like
ben_01's avatar

@strmizo you can merge the route parameter to your request class like this,

    public function functionName(Request $request, $parameter) {
        $request->merge(['parameter' => $parameter]) ;
    }   
dev@eyecarepro.net's avatar

In Laravel 6, this worked for me

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

By putting the merge in this order, the route params take precedence over the request input. So if there is invalid input in the param AND they also included a query string with the same variable name, the validation will still fail. If you put them in reverse order, then what should be an invalid URL will work... so unless you want that...

1 like

Please or to participate in this conversation.