Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel 7 - Scoping problem in Nested Resource Route

Routes:

I have a nested resource route definition like this:

Route::resource('posts.comments', 'CommentController');

That produces the following routes:

+--------+-----------+--------------------------------------+------------------------+------------------------------------------------+------------+
| Domain | Method    | URI                                  | Name                   | Action                                         | Middleware |
+--------+-----------+--------------------------------------+------------------------+------------------------------------------------+------------+
|        | GET|HEAD  | posts/{post}/comments                | posts.comments.index   | App\Http\Controllers\CommentController@index   | web        |
|        | POST      | posts/{post}/comments                | posts.comments.store   | App\Http\Controllers\CommentController@store   | web        |
|        | GET|HEAD  | posts/{post}/comments/create         | posts.comments.create  | App\Http\Controllers\CommentController@create  | web        |
|        | GET|HEAD  | posts/{post}/comments/{comment}      | posts.comments.show    | App\Http\Controllers\CommentController@show    | web        |
|        | PUT|PATCH | posts/{post}/comments/{comment}      | posts.comments.update  | App\Http\Controllers\CommentController@update  | web        |
|        | DELETE    | posts/{post}/comments/{comment}      | posts.comments.destroy | App\Http\Controllers\CommentController@destroy | web        |
|        | GET|HEAD  | posts/{post}/comments/{comment}/edit | posts.comments.edit    | App\Http\Controllers\CommentController@edit    | web        |
+--------+-----------+--------------------------------------+------------------------+------------------------------------------------+------------+

Relationships (In Models):

Post model:

public function comments()
{
    return $this->hasMany(Comment::class);
}

Comment model:

public function post()
{
    return $this->belongsTo(Post::class);
}

Dummy Data (In Tables):

posts table:

+----+--------+-----------------------------+---------------------+---------------------+
| id | title  | body                        | created_at          | updated_at          |
+----+--------+-----------------------------+---------------------+---------------------+
| 1  | Post 1 | This is the body of Post 1. | 2020-07-29 11:20:53 | 2020-07-29 11:20:53 |
| 2  | Post 2 | This is the body of Post 2. | 2020-07-29 11:21:13 | 2020-07-29 11:21:13 |
+----+--------+-----------------------------+---------------------+---------------------+

comments table:

+----+---------+-----------------------------+---------------------+---------------------+
| id | post_id | body                        | created_at          | updated_at          |
+----+---------+-----------------------------+---------------------+---------------------+
| 1  | 1       | The comment for the Post 1. | 2020-07-29 11:22:27 | 2020-07-29 11:22:27 |
| 2  | 2       | The comment for the Post 2. | 2020-07-29 11:22:32 | 2020-07-29 11:22:32 |
+----+---------+-----------------------------+---------------------+---------------------+

In the docs:

When using a custom keyed implicit binding as a nested route parameter, Laravel will automatically scope the query to retrieve the nested model by its parent using conventions to guess the relationship name on the parent.

So, {comment} is supposed to be child of {post}. But when I hit /posts/1/comments/2, it retrieves comment with an id of 2 which belongs to the post with an id of 2. The expected result would be NotFoundHttpException.

It works fine when I define the routes individually like this:

Route::get('/posts/{post}/comments/{comment:id}', 'CommentController@show');

Why is this happening?

Also tried to customize the default key name in both Post and Comment models:

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

But no luck.

Any help would be appreciated.

like image 959
N'Bayramberdiyev Avatar asked Jul 29 '20 10:07

N'Bayramberdiyev


1 Answers

Did some digging and reached a conclusion after reading Illuminate\Routing\PendingResourceRegistration.php class from the source code. I have to use custom keyed implicit binding to make it work as I expect.

Route::resource() method takes (optional) third argument which is an associative array. So, I need to override the route parameter name via parameters key using this argument.

Route::resource('posts.comments', 'CommentController', [
    'parameters' => ['comments' => 'comment:id'],
]);

or

Route::resource('posts.comments', 'CommentController')->parameters([
    'comments' => 'comment:id',
]);

It works either way.

like image 171
N'Bayramberdiyev Avatar answered Nov 08 '22 19:11

N'Bayramberdiyev