Framework: apiResource name clash

Created on 27 Apr 2018  Â·  8Comments  Â·  Source: laravel/framework

  • Laravel Version: v5.6.17
  • PHP Version: 7.2
  • Database Driver & Version: MySQL

Description:

When using Route::apiResource method if you have 2 routes that end in the same URL segment, then it causes a name clash regardless of the URL, and there does not seem to be a way to define the namespace for the route, within the docs at least.

Steps To Reproduce:

Define 2 apiResources

Route::apiResource('users/{user}/comments', 'Comments\UserCommentController');
Route::apiResource('posts/{post}/comments', 'Comments\PostCommentController');

Then the named routes end up being the same

➜ php artisan route:list --name=comments
+--------+-----------+---------------------------------------------------+------------------+-----------------------------------------------------------------------+------------+
| Domain | Method    | URI                                               | Name             | Action                                                                | Middleware |
+--------+-----------+---------------------------------------------------+------------------+-----------------------------------------------------------------------+------------+
|        | GET|HEAD  | api/users/{user}/comments              | comments.index   | App\Http\Controllers\API\Comments\UserCommentController@index                    | api        |
|        | POST      | api/users/{user}/comments              | comments.store   | App\Http\Controllers\API\Comments\UserCommentController@store                    | api        |
|        | GET|HEAD  | api/users/{user}/comments/{comment}    | comments.show    | App\Http\Controllers\API\Comments\UserCommentController@show                     | api        |
|        | PUT|PATCH | api/users/{user}/comments/{comment}    | comments.update  | App\Http\Controllers\API\Comments\UserCommentController@update                   | api        |
|        | DELETE    | api/users/{user}/comments/{comment}    | comments.destroy | App\Http\Controllers\API\Comments\UserCommentController@destroy                  | api        |
|        | GET|HEAD  | api/posts/{post}/comments              | comments.index   | App\Http\Controllers\API\Comments\PostCommentController@index                    | api        |
|        | POST      | api/posts/{post}/comments              | comments.store   | App\Http\Controllers\API\Comments\PostCommentController@store                    | api        |
|        | PUT|PATCH | api/posts/{post}/comments/{comment}    | comments.update  | App\Http\Controllers\API\Comments\PostCommentController@update                   | api        |
|        | DELETE    | api/posts/{post}/comments/{comment}    | comments.destroy | App\Http\Controllers\API\Comments\PostCommentController@destroy                  | api        |
|        | GET|HEAD  | api/posts/{post}/comments/{comment}    | comments.show    | App\Http\Controllers\API\Comments\PostCommentController@show                     | api        |
+--------+-----------+---------------------------------------------------+------------------+-----------------------------------------------------------------------+------------+

Then if you try and swap the params around, you get a more awkward name

Route::apiResource('comments/user/{user}', 'Comments\UserCommentController');
Route::apiResource('comments/post/{post}, 'Comments\PostCommentController');

You will end up with

{user}.index,
{user}.show,
{user}.store,
{user}.destroy,
{user}.update,

It's almost like you need to be able to "name" the resource, which if used will prefix them, rather than having to do something really verbose like

Route::apiResource('user/{user}/comments', 'Comments\UserCommentController')
->names([
    'index' => 'comments.users.index',
    'show' => 'comments.users.show',
    'store' => 'comments.users.store',
    'update' => 'comments.users.update',
    'destroy' => 'comments.users.destroy',
]);

Most helpful comment

This is from memory - but with resource you can pass options as the 3rd parameter to set a prefix for the named routes. This should leave you with named routes like users.comments.index.

Route::apiResource('users/{user}/comments', 'Comments\UserCommentController', [
    'as' => 'users'
]);

All 8 comments

If i remember correctly, you could use

Route::apiResource('user.comments', 'Comments\UserCommentController');

This would result in user.comments.* route names

@koenhoeijmakers the first param is the route pattern, so that wouldn’t work - and it doesn’t allow for the first param

It generates routes like:

user.comments.index -> user/{user}/comments

@koenhoeijmakers

Ha, that's weird, do you know if there's somewhere on the docs that explain all this? I guess it works okay if your route param, matches the model name.

I guess it is definitely a workaround, however, it would still make sense I feel to be able to name the route like you can do for normal routes

https://laravel.com/docs/5.1/controllers#restful-nested-resources

It was in the 5.1 docs, but it was removed from the docs from 5.2 and up, as Taylor doesn't think of it as that good of a practice (although to me it makes perfect sense for your situation). source

This is from memory - but with resource you can pass options as the 3rd parameter to set a prefix for the named routes. This should leave you with named routes like users.comments.index.

Route::apiResource('users/{user}/comments', 'Comments\UserCommentController', [
    'as' => 'users'
]);

Can confirm the latter, which is a better solution than mine.

@dwightwatson will check this out today see how ignorant plays out :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

shopblocks picture shopblocks  Â·  3Comments

klimentLambevski picture klimentLambevski  Â·  3Comments

ghost picture ghost  Â·  3Comments

CupOfTea696 picture CupOfTea696  Â·  3Comments

Anahkiasen picture Anahkiasen  Â·  3Comments