3

I'm developing an API with Laravel 6.

I've got 2 models:

card -> table cards with card_id ecc.

user -> table users with user_id ecc.

I've defined into models many to many relationships

User.php

public function cards()
{
    return $this->belongsToMany('App\Models\v1\Card');
}

Card.php

public function users() {
    return $this->belongsToMany('App\Models\v1\User');
}

The pivot table is called card_user .

Now I've created routes for single entities:

Route::resource('v1/users', 'v1\UsersController');
Route::resource('v1/cards', 'v1\CardsController');

and I need to develop routes and controller for insert and delete rows from pivot table.

What is the best practice for this issue?

I try to solve this with a special controller that respond to a specific endpoint:

Route::resource('v1/cards/{id}/users', 'v1\CardsUsersController')->only([
    'index', 'store', 'destroy'
]);

But when I need to store information I need to pass the ids of card and user into the URL and as object in post body like so:

[
    'user_id' => $userId,
    'card_id' => $cardId
]

Exists a better way to do this?

Thanks a lot!

BobbyLinux
  • 140
  • 1
  • 12

3 Answers3

4

You can use Nested Resources as described here: https://laravel.com/docs/6.x/controllers#restful-nested-resources

"Sometimes you may need to define routes to a "nested" resource. For example, a photo resource may have multiple "comments" that may be attached to the photo. To "nest" resource controllers, use "dot" notation in your route declaration:

Route::resource('photos.comments', 'PhotoCommentController');

This route will register a "nested" resource that may be accessed with URLs like the following: photos/{photos}/comments/{comments}."

Devon Bessemer
  • 34,461
  • 9
  • 69
  • 95
2

If you must have separate routes and controller for them, then it would be better to do

Route::resource('v1/card_user', 'v1\CardsUsersController')->only(['index', 'store','destroy']);

Keep the route clean, and don't overcomplicate it. Either You or someone else in the future who views code should be able to understand what it is for.

Arun A S
  • 6,421
  • 4
  • 29
  • 43
  • 2
    The standard way is to use resource/id/sub-resource/sub-resource-id – Devon Bessemer Feb 06 '20 at 15:01
  • Thanks! and what kind of resource can i return from the routes? I've created a CardUser resource and return something like this: {#1279 +"card_user": {#1216 +"card_id": 1 +"card_code": "95196" +"enabled": 1 +"users": array:1 [ 0 => {#1272 +"id": 1 +"last_name": "Lubowitz" +"first_name": "Beau" +"username": "beau.lubowitz" } ] } +"status": "success" +"code": 201 } – BobbyLinux Feb 06 '20 at 15:31
  • @RobertoBani if you follow my example, then your CardUser Resource should have both card and user as seerate entities. Btw, it seems this is not the standard method so I think you should use Devons answer for best practices. I suggested this method because I personally prefer keeping url's short. [This question](https://stackoverflow.com/q/20951419/4516316) will help you better understand – Arun A S Feb 07 '20 at 04:16
1

I would combine both answers. As a relationship, it is technically a nested resource. Also, you really have 2 RESTful actions: store and destroy (which correspond to attach and detach in Laravel). You may also want an index to view all of the relationship. I believe the "create" action is optional, depending on your UI.

// Ability_Role pivot routes
Route::resource('v1/user.cards', 'UserCardController')
  ->only(['index', 'create', 'store','destroy']);

This will give the following routes:

+--------+----------+------------------------------+--------------------+-------------------------------------------------+------------+
| Domain | Method   | URI                          | Name               | Action                                          | Middleware |
+--------+----------+------------------------------+--------------------+-------------------------------------------------+------------+
|        | GET|HEAD | v1/user/{user}/cards         | user.cards.index   | App\Http\Controllers\UserCardController@index   | web        |
|        | POST     | v1/user/{user}/cards         | user.cards.store   | App\Http\Controllers\UserCardController@store   | web        |
|        | GET|HEAD | v1/user/{user}/cards/create  | user.cards.create  | App\Http\Controllers\UserCardController@create  | web        |
|        | DELETE   | v1/user/{user}/cards/{card}  | user.cards.destroy | App\Http\Controllers\UserCardController@destroy | web        |
+--------+----------+------------------------------+--------------------+-------------------------------------------------+------------+

I chose to label the routes as user.cards because I would think you would more often want to start with the user model and attached the cards.

For the store method, you can post an array of cards to attached to the user.

If you also want to start with cards, and store an array of users, you can also define the inverse relationships (though it would require a 2nd controller with just the create and store routes:

// Inverse create and store routes
Route::get('v1/cards/{card}/users/create', 'CardUserController@create')
  ->name('cards.users.create');
Route::post('v1/cards/{card}/users', 'CardUserController@store')
  ->name('cards.users.store');

now you will get 2 more routes added:

+--------+----------+------------------------------+--------------------+-------------------------------------------------+------------+
| Domain | Method   | URI                          | Name               | Action                                          | Middleware |
+--------+----------+------------------------------+--------------------+-------------------------------------------------+------------+
|        | GET|HEAD | api/user                     | api.               | Closure                                         | api        |
|        |          |                              |                    |                                                 | auth:api   |
|        | POST     | v1/cards/{card}/users        | cards.users.store  | App\Http\Controllers\CardUserController@store   | web        |
|        | GET|HEAD | v1/cards/{card}/users/create | cards.users.create | App\Http\Controllers\CardUserController@create  | web        |
|        | GET|HEAD | v1/user/{user}/cards         | user.cards.index   | App\Http\Controllers\UserCardController@index   | web        |
|        | POST     | v1/user/{user}/cards         | user.cards.store   | App\Http\Controllers\UserCardController@store   | web        |
|        | GET|HEAD | v1/user/{user}/cards/create  | user.cards.create  | App\Http\Controllers\UserCardController@create  | web        |
|        | DELETE   | v1/user/{user}/cards/{card}  | user.cards.destroy | App\Http\Controllers\UserCardController@destroy | web        |
+--------+----------+------------------------------+--------------------+-------------------------------------------------+------------+
docTRIN
  • 71
  • 1
  • 4