Question

I would like to be able to accept either a 40-character string ID /users/{id} or the user's username /users/{username} and then return the users.show view.

  1. Is this possible?
  2. Where would the checks go?
Was it helpful?

Solution

I have found another solution to this. I don't know which is better though. Hopefully the community can vote...

Within my UsersController.php I have:

public function show($id_or_username)
    {
        $user = User::where('id' , '=', $id_or_username)->orWhere('username', $id_or_username)->firstOrFail();
        return View::make('users.show', compact('user'));
    }

OTHER TIPS

Implicit Model Binding

For those who are looking for the Laravel 5.3+ answer:

Jeffery Way talked about this in one of his laracasts; not sure which one though.

Basically, you can pass the username through a route like this:

// you will need to set this up so the route ('/user/{user}')
// matches the Model you're binding to -> App\<User>
// ('/user/{username}') will not work 

Route::get('/user/{user}', function(App\User $user){
    // $user should now be App\User
    return view('user.show', compact('user'));

    // access the user in the 'user.show' view
    // like: $user->username
});

However, to make this work, you need to add this to the User class:

public function getRouteKeyName()
{
    return 'username';
}

Otherwise this would only work if you passed the id of the user in place of the username -> /user/1

I would go about this with Explicit Route Model Binding.

To do this:

  1. Use the Route::bind method to specify custom binding logic

    To do this add the following code to the boot method of the RouteServiceProvider class.

    Route::bind('user', function ($value) {
        return \App\User::where('id', $value)->orWhere('username', $value)->first();
    });
    
  2. Set up your route for Route Model Binding

    Route::get('/{user}', 'UserController@show'); 
    
  3. Modify your controller to accept the bound model

    public function show(User $user)
    {
        echo $user;
    }
    

There's a couple of advantages here over the other answers:

  • Using the route model binding rather than having the finding logic within your show method will make it more reusable if you want to apply the same logic to different requests.
  • No need for a separate showByUserName and showByUserID method.
  • Using getRouteKeyName will only work for either ID or username, you can't use it to specify multiple keys.

When I need to match certain patterns, I use the Route::pattern() method in the top of my routes.php file. For example:

Route::pattern('userid', '[0-9]{40}');
Route::pattern('username', '[a-zA-Z0-9]+');

You can easily adapt that to use your own regular expression to match whatever you need and then you just create two routes to match the requests:

Route::get('/users/{userid}', 'UserController@showByUserID');
Route::get('/users/{username}, 'UserController@showByUserName');

Those two controller methods would get the user differently, but both would call the same view file and pass in the user.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top