luisfer
luisfer

Reputation: 2120

Laravel routing, is this the expected behavior?

Not actually a problem, I think I can handle, but I want to know if this is behaving as expected.

I have a database table named users, then I created the Model and the UserController as a resource with

php artisan make:controller UserController --resource

It created the expected routes (index, store, create, show, update, destroy and edit), some GET some POST.

So now I want to create a new route that gives me the user's avatar (an image I previously stored using the Storage methods)

So I create it in the /routes/web.php file as:

Route::get('/user/avatar/{id?}', 'UserController@avatar')->name('user.avatar');

I know the function works, if I go to http://localhost/user/avatar/1 it returns the image associated to user 1. Now the thing is, I want the parameter to be optional (nullable), if I don't get the id then I'll serve the \Auth::user()->id image.

The problem is whwn I go to http://localhost/user/avatar/ it gaves me an error (I won't print it here, because it isn't related), it looks like it is trying to go to the GET's /user/{id} route that was created with the resource, and it is treating "avatar" as the {id}.

So I know, I should take out the line Route::resource('user', 'UserController'); and create each route individually, right?

My question is, is this how it is supposed to work? should I create other, say, HelperController where I could point an /avatar/{id?} route?

EDIT: The artisan route:list output regarding to user is:

|        | GET|HEAD  | user                   | user.index       | App\Http\Controllers\UserController@index                              | web          |
|        | POST      | user                   | user.store       | App\Http\Controllers\UserController@store                              | web          |
|        | GET|HEAD  | user/avatar/{id?}      | user.avatar      | App\Http\Controllers\UserController@avatar                             | web          |
|        | GET|HEAD  | user/create            | user.create      | App\Http\Controllers\UserController@create                             | web          |
|        | GET|HEAD  | user/{user}            | user.show        | App\Http\Controllers\UserController@show                               | web          |
|        | PUT|PATCH | user/{user}            | user.update      | App\Http\Controllers\UserController@update                             | web          |
|        | DELETE    | user/{user}            | user.destroy     | App\Http\Controllers\UserController@destroy                            | web          |
|        | GET|HEAD  | user/{user}/edit       | user.edit        | App\Http\Controllers\UserController@edit                               | web          |
+--------+-----------+------------------------+------------------+------------------------------------------------------------------------+--------------+

Error is:

 ErrorException (E_ERROR)
Trying to get property 'name' of non-object (View: D:\Dropbox\_www\reco\resources\views\user\profile.blade.php)
Previous exceptions

    Trying to get property 'name' of non-object (0)

This is in a $user->name call, $user is undefined, and I know what causes it, it is the find($id) inside show (in the Controller) which is getting nothing, because it is searching for "avatar" as id.

In fact, if I change the find() for findOrFail() it gives me the expected 404 error. So I'm pretty sure that it is interpreting avatar as the id.

Upvotes: 1

Views: 415

Answers (1)

Criss
Criss

Reputation: 983

You have to define your most explicit routes first:

Route::get('/user/avatar/{id?}', 'UserController@avatar');
Route::resource('user', 'UserController');

Why? Because route resource creates a GET user/{user} route and if you register resource route first, Laravel will look at the url user/avatar/1 and think that the avatar part is actually the ID of user.

It's just a quirk of laravel.

Upvotes: 5

Related Questions