A nested resource is a resource with another sub-resource (eg: one-to-many relationship). For instance, a podcast may have multiple episodes. The nested resource of podcast and episodes can be defined as below in Laravel:
use App\Http\Controllers\PodcastEpisodesController;
Route::resource('podcast.episodes', PodcastEpisodesController::class);
The URIs defined in route will be like this:
/podcast/{podcast}/episodes/{episode}
A similar example in official Laravel 8.x documentation here: https://laravel.com/docs/8.x/controllers#restful-nested-resources
Here are 4 tips shared by Adam Wathan on strategies to split large controllers into multiple small controllers.
Tip 1: Give nested resources a dedicated controller
Instead of creating a custom methods in PodcastController
or EpisodeController
, we can create a dedicated controller which is PodcastEpisodesController
. In this controller, we can place index method to view all episodes under a specific podcast and place create & store methods to view create episode page and store the new episode under a podcast.
Route::get('/podcasts/{id}/episodes', 'PodcastEpisodesController@index');
Route::post('/podcasts/{id}/episodes', 'PodcastEpisodesController@store');
Route::get('/podcasts/{id}/episodes/new', 'PodcastEpisodesController@create');
Tip 2: Treat properties edited independently as separate resources
Resources exposed through your controllers and endpoints don’t have to map one-to-one with your models or database tables.
If a property of a model is being updated separately, we can introduce another controller specifically for this property. In the example given by Adam, he created PodcastCoverImageController
and utilize the update method.
Tip 3: Treat pivot models as their own resource
Sometimes, a pivot model can be treated as an entirely new model. In the example shown by Adam, a podcast can be subscribed by multiple users and a user can subscribe to many podcasts. Instead of having podcast_user pivot table, he introduce a new model which is subscription model to replace the pivot table. With that, he can treat subscription as their own resource and use store and destroy methods to add and remove subscription.
- Route::post('/podcasts/{id}/subscribe', 'PodcastsController@subscribe');
+ Route::post('/subscriptions', 'SubscriptionsController@store');
- Route::post('/podcasts/{id}/unsubscribe', 'PodcastsController@subscribe');
+ Route::delete('/subscriptions/{id}', 'SubscriptionsController@destroy');
Tip 4: Think of different states as different resources
Even for different states such as publish or unpublish (published_at column in table), we can think of it as different resources. We can introduce a new controller called PublishedPodcastsController. In this controller, we utilize store method to update the published_at column and destroy method to remove the value in published_at column.
- Route::post('/podcasts/{id}/publish', 'PodcastsController@publish');
+ Route::post('/published-podcasts', 'PublishedPodcastsController@store');
- Route::post('/podcasts/{id}/unpublish', 'PodcastsController@unpublish');
+ Route::delete('/published-podcasts/{id}', 'PublishedPodcastsController@destroy');