Laravel Tip: Write more controllers

While listening to FullStack Radio, I heard Taylor Otwell give a great piece of advice about how to name controllers and their respective methods. I thought it deserved its own write up as it's proven seriously useful in keeping my controllers readable.

When working in a team, you should not be able to tell who’s written a piece of code. Every developer codes to the same guidelines and coding style.

While this goal is often hard to reach in reality, we can take steps to get closer to fulfilling it. Some common techniques include: linting your code or following a documented coding style (like PSR2). However, due to everybody having different vocabularies, naming is the hardest thing to keep consistent within a team. Creating conventions for common solutions is a great way to improve this situation.

Here, I’ll be talking about how you should name your controllers and their respective methods. Doing so will keep your controllers smaller, easier to understand and consistent within the team. I first heard about this from Taylor Otwell, who was inspired by a great talk by David Heinemeier.

The convention consists of 2 key concepts:

  1. Write more controllers
  2. Only use 7 REST verbs in your controllers

Write more controllers

People seem to be adverse to having too many controllers, and tend to favour a fewer number of controllers with more methods on. For example, you could have the following:

A BasketController with the methods:

  • createBasket()
  • getBasket()
  • addProduct()
  • updateProduct()
  • removeProduct()

The problem here is that this violates the Single Responsibility Principle. Your controller cares about 2 things: the basket itself and products in the basket.

The simple solution here is to have 2 controllers:

A BasketController with the methods:

  • createBasket()
  • getBasket()

A BasketProductController with the methods:

  • addProduct()
  • updateProduct()
  • removeProduct()

Now each controller only has one responsibility and as a result are much smaller and more focused. But how do you know when to apply this? That leads me onto the second concept…

Only use the 7 REST verbs in your controllers

These are: index, create, store, show, edit, update, destroy.

Laravel has some great documentation on when each action should be used depending on the url and verb:

How to use REST verbs for resources

Using these, we can refactor our original controller:

Our BasketController:

  • createBasket() becomes create()
  • getBasket() becomes show()
  • addProduct() cannot use one of those verbs
  • updateProduct() cannot use one of those verbs
  • removeProduct() cannot use one of those verbs

Here you can see that it’s impossible to name any of the product related methods using only the 7 verbs – as they’ll clash with the basket. The solution? You guessed it, create a new controller:

Add a BasketProductController and change the methods to:

  • addProduct() becomes store()
  • updateProduct() becomes update()
  • removeProduct() becomes destroy()

Now we have 2 controllers, where all the methods exclusively use one of the seven REST verbs. We now have something that looks like this:

A BasketController with the methods:

  • create()
  • show()

A BasketProductController with the methods:

  • store()
  • update()
  • destroy()

What happens if something doesn’t fit into this model?

It happens. Sometimes you are doing something that doesn’t follow the standard CRUD model. I often run into this when adding RPC style endpoints alongside a REST api.

For example, I will need to convert this basket to an order. So I allow the following URL:

POST /api/baskets/1/actions/convert-to-order

There are several solutions we could apply here. Pick one or come up with our own, but stick with one convention.

Create a BasketOrderController with a convert method on.

Personally I don’t like this method as it’s often difficult to name the new controller. And here, is it the order or the basket that is being converted? It’s difficult to tell by the name alone.

Add a convertToOrder method on the BasketController

This is fine. A small number of additional methods on the controller for non-CRUD operations is a good compromise. But sometimes it can still get a bit out of hand, especially as you’ll likely have some helper methods alongside it, bloating your controller.

Stick related things together together: e.g. BasketActionController

This is what I am currently doing. It keeps my CRUD controllers clean and simple and I can keep the potential mess contained to one place. If you’re dealing with reports for a basket, you could create a BasketReportController. It doesn’t always fit, but it helps keep my other controllers clean.

Wrapping up

Whether you agree or disagree with points in this post, the most important thing is that you work out what you like and stick with it. The project I’m working on at the moment has 38 controllers, each with an average of 4-5 methods on. Out of those, I have 7 of these <thing>ActionControllers. It feels much cleaner and easier to navigate and I’d highly recommend it.

Bonus tip: Always use singular names for controllers. BasketController not BasketsController. BasketProductController not BasketsProductsController or BasketProductsController. It gets real confusing knowing when to apply the plural or not and leads to inconsistencies.