Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

manuelro's avatar

Nested resources controllers structure

I've been using Laravel since a few months and it's an awesome framework, but I have a doubt regarding resource controllers and the proper structure for nested controllers.

When you register a new resource route, for example 'foo' (with FooController), and then you register another one ('bar', BarController) everything is fine, until you start needing to nest them as foo.bar.

Laravel docs suggest you to use a different controller for this relation, named FooBarController; but what about if we decide that we need to get two more levels in the in depth? Something like foo.bar.bez.biz, then we find ourselves with four controllers:

    Route::resource('foo', 'FooController')
    Route::resource('foo.bar', 'FooBarController')
    Route::resource('foo.bar.bez', 'FooBarBezController')
    Route::resource('foo.bar.bez.biz', 'FooBarBezBizController')

Your controllers folder is suddenly overpopulated with long-hard-to-write controller names. Here is where I start thinking I'm doing something wrong and this is why I'm asking for some advice.

Is it OK to have that bunch of controllers? I've been thinking about having a single FooController (BarController, BezController, etc) and handle everything that way, but I'm afraid this isn't clear enough when you work with more people.

Thanks, M

0 likes
23 replies
bobbybouwmann's avatar

I think this should be clear enough ;) You always for with bez as an example and then you know you have to use the bez controller ;)

Route::resource('foo', 'FooController');
Route::resource('foo.bar', 'BarController');
Route::resource('foo.bar.bez', 'BezController');
Route::resource('foo.bar.bez.biz', 'BizController');
2 likes
pmall's avatar

Is it OK to have that bunch of controllers? I've been thinking about having a single FooController (BarController, BezController, etc) and handle everything that way, but I'm afraid this isn't clear enough when you work with more people.

Yes it is ok and it is the way to handle it. The name of the controller doesn't matter as @bobbybouwmann said but this is the structure you should use.

Lets say Foo and Bar can be stored. You have a store method per controller, no funny action names in one controller.

bobbybouwmann's avatar

I want to explain myself a bit more on this. Let's say you have a Post and Comment model. In your case you might end up with something like this since a comment belongs to a post

Route::resource('posts', 'PostsController');
Route::resource('posts.comments', 'PostsCommentsController');

This is much more readable and easier to understand

Route::resource('posts', 'PostsController');
Route::resource('comments', 'CommentsController');

Just note that the naming of the controllers should be unique! Don't end up with stuff like this

Route::resource('posts', 'PageController'); // Person one uses this convention
Route::resource('comments', 'PagesController'); // Person two uses this convention

The example above will become a mess! Make sure you have convention rules in your team ;)

1 like
manuelro's avatar

Short names based on the last entity seems to be OK, but adhering to REST principles where there are not action methods but resources and collections I may end up having a set of routes as follows:

Route::resource('foo', 'FooController');

// Specific methods for the relation between models
Route::resource('foo.bar', 'FooBarController'); // For accessing Bar related with Foo
Route::resource('foo.bar.bez', 'FooBarBezController'); // For accessing Bez related with Foo and Bar
Route::resource('foo.bar.bez.biz', 'FooBarBezBizController'); // For accessing Biz related with Foo, Bar and Bez

// General methods for each collection/resource
Route::resource('bar', 'BarController'); // For accessing bar
Route::resource('bez', 'BezController'); // For accessing bez
Route::resource('biz', 'BizController'); // For accessing biz

This tells me I'll have to use the long-named controllers since handling everything with a single controller breaks the REST conventions of doing things, for example, I will end up having two store methods, one for the relation (BarController@store and BarController@fooBarStore), all of this without mentioning that I'll also have to register every extra method as an extra route.

bashy's avatar

Most of the time you wouldn't have such long URIs, you got an example of the structure you're looking for?

sebdesign's avatar
Level 29

Instead of using extra long-named controllers, I prefer to namespace them like this:

Route::resource('foo', 'FooController');

// Specific methods for the relation between models
Route::resource('foo.bar', 'Foo\BarController'); // For accessing Bar related with Foo
Route::resource('foo.bar.bez', 'Foo\Bar\BezController'); // For accessing Bez related with Foo and Bar
Route::resource('foo.bar.bez.biz', 'Foo\Bar\Bez\BizController'); // For accessing Biz related with Foo, Bar and Bez

// General methods for each collection/resource
Route::resource('bar', 'BarController'); // For accessing bar
Route::resource('bez', 'BezController'); // For accessing bez
Route::resource('biz', 'BizController'); // For accessing biz

Following a PSR-4 convention, that would result in a directory structure that reflects the RESTful resource hierarchy. Also the filenames would be shorter and the base Http\Controllers directory a lot cleaner.

10 likes
manuelro's avatar

I think this can be a good example:

We have three entities (Person, Place, Review) with the following relations:

  • Person - Review
    • A Person has zero or more Reviews
    • A Review belongs to one Person
  • Place - Review
    • A Place has zero or more Reviews
    • A Review belongs to one Place

Based on the previously mentioned relations, we can come up with the following resource routes:

    // For handling Person specific REST methods
    Route::resource('person', 'PersonController');
    
    // For handling Person-Place-Review relation
    Route::resource('person.place.review', 'PersonPlaceReviewController'); 
    
    // For place-specific REST methods
    Route::resource('place', 'PlaceController'); 

    // For collection listing purposes
    Route::resource('place.review', 'PlaceReviewController');

I'm quite new to the REST architecture back-end development, but I have experience as a front-end consumer. So, based on this the most accurate way of organizing the controllers of a project will be by using the long relation name.

sebdesign's avatar

I'm not sure about the Person-Place-Review relation.

The Review belongs to a Person, and also belongs to a Place. That would be expressed by the following resources:

    // For handling Person specific REST methods
    Route::resource('person', 'PersonController');
    
    // For reviews in the context of a person
    Route::resource('person.review', 'Person\ReviewController'); 
    
    // For place-specific REST methods
    Route::resource('place', 'PlaceController'); 

    // For reviews in the context of a place
    Route::resource('place.review', 'Place\ReviewController');
1 like
manuelro's avatar

I think the person.place.review relation is important from a REST-like programming style perspective.

For example, let's take a look at how a store method would look like for this relation:

    // Path: person/{person}/place/{place}/review
    public function store($personId, $placeId) {
        //
    }

Not including place in the middle would break the REST pattern:

    // Path: person/{person}/review
    public function store($personId) {
        //
    }

While you can easily use any of the two ways shown above (using the second one with URL parameters), I'd rather go with the first one (person.place.review), it is easier to grab all the related entities IDs and looks more REST styled to me.

sebdesign's avatar

I don't have much experience with the REST architecture. Personally I would just store the review using the place.review.store POST method.

<?php namespace App\Place;

use App\Place;
use App\Review;
use App\Http\Requests\AddReviewRequest;

class ReviewController {
    // place.review.store
    public function store(AddReviewRequest $request, Place $place, Review $review) {
        // fill any review-related data
        $review->fill($request->all());

        $review->person()->associate(\Auth::user());
        $review->place()->associate($place);

        $review->save();
    }
}

The $personId could be resolved through the Auth service, as I guess that you want to store a review by an authenticated person, and not any person. Although I'm not sure if that follows the REST paradigm.

2 likes
leandromatos's avatar

Resurrecting the conversation. @sebdesign , how the organize models and repositories nesting? You separated by namespaces according to the controllers too?

Sorry for my bad english ;)

sebdesign's avatar

Hi @leandromatos, I don't have much experience with repositories, but I like to namespace my models according to the controllers nesting.

For example, a Person model and a Place model sound like first-class citizens, but a Review model belongs to a Place and a Person. In that case I would namespace it under App\Person\Review or App\Place\Review.

Of course you can't have both, so I would pick the structure that would make more sense for the application, or the one that is mostly used. E.g. if your app focuses on places, you might already have a App\Place namespace which has other entities under its belt, like photos, address, checkins (or Pokémons).

1 like
goopil's avatar

You don't need multiple repositories for the same resource (like you don't need a model for each case) (the repository pattern is used to reduce duplicate code if i remember correctly ;))

if you need filtering just push a criteria and if you need a specific output format add a transformer for this case.

have a look at that great package if you haven't already andersao/l5-repository

1 like
leandromatos's avatar

Excellent package @goopil !

Imagine that I have a PostRepository and want to bring customized results of the database, it would be easier to create a public method in PostRepository than a new Criteria?

The criteria would be for what exactly?

leandromatos's avatar

Analyzing this package some rules SOLID was broke. I will create something following the repositories pattern without violating these rules. Anyway, thanks for the tip.

1 like
goopil's avatar

When your app is growing in complexity and somes models are reused in multiple place. It let you abstract away a query scope (for exemple) and register them when you need it.

In short the criteria are an object based container for query scope (well, it's much more, but lets keep it simple for now)

if you need some more explanation on criteria let me know.

if you intend to develop an alternative to this package, i would gladly contribute ;) I know its not always SOLID but regidity is not always the best solution.

"It depend" like always in IT...

1 like
ricardovigatti's avatar

@leandromatos andersao's package isn't that good how people always says. Actually, any of those repository packages aren't good for Laravel because they are coupled with Eloquent.

I know you don't asked for that, but i can give you my advice to not use the repository pattern with Eloquent. Try implement the repository pattern directly with Query Builder or Doctrine 2.

Cheers.

notflip's avatar

What I you need the nesting, for example

/admin/clients/cocacola/categories/general/questions/1

How would we go about this? or is using a resource controller the best option? How about custom methods? thanks!

SupaMonkey's avatar

Hi @sebdesign, How would you go about making this controller: Route::resource('foo.bar.bez', 'Foo\Bar\BezController');

Using php artisan make:controller Foo\Bar\BezController --resource ends up with a FooBarBezController file anyway? Would you just 'make' the BezController and move it to a Foo\Bar\ directory?

Thanks in advance

SupaMonkey's avatar

For those looking for an answer to my question above; its achievable in various ways:

Quote:

php artisan make:controller "Mobile\SomeController"

Escape:

php artisan make:controller Mobile\\SomeController

Forwardslash:

php artisan make:controller Mobile/MobileController

Please or to participate in this conversation.