Refactoring #2: From controllers to actions

Geni Jaho
2 min readDec 24, 2021


A major pain point in programming is having to deal with all the clutter that is shoved into controller methods. One of the best ways to remedy that is extracting some pieces of functionality into their own classes. They’re called Actions, or Services, or whatever.

Their goal is simple: to do one thing, and one thing only. Using them comes with a few benefits. Firstly, the controllers are much easier to read and understand. Secondly, these classes are reusable, you could use them in other controllers, or console commands. The trick here is to keep them awkwardly simple.

Take the controller head-on

Our use case is simple: a user wants to join a team. Here’s the controller method that we want to refactor.

Could be better, right? The first thing we want to do is create this new action.

You’ll notice we’ve excluded anything related to request handling, or response output. That’s because that is better handled at the controller level. And that’s how we’re keeping actions simple. We could’ve extracted another action out of this one, for sure, but for the time being, nah, it’ll be fine.

Refactor into FormRequests

There’s one more thing we should do with our controller though. We can extract that form validation into its own FormRequest. Helps the readability a bit.

If you’re wondering about the changed signature, and that exists rule, it's a nice laravel helper.

The final result is a much cleaner controller, where it’s easier to understand what’s going on.

Use Actions in other places as well

And the best part is, we wanted to use the same functionality on the API for our mobile app. It had some differences in how we output results, so we had to use another controller. Sure enough, actions helped in the process. Here’s how the API method looks like with actions:

Refactoring this into a single controller is going to be a breeze since most of the logic is already reusable.

Note that we already have tests written beforehand for this refactoring to happen safely. They’re a huge plus when dealing with these types of issues. Also, check this amazing post on using Actions from guys at Spatie.

As an extra, one could also work with Spatie’s Actions package to make them queueable, or even the full-fledged Laravel Actions package. We kept it simple for now using Plain Old Objects in PHP (POOP).

The code used for illustration is taken from the OpenLitterMap project. They’re doing a great job creating the world’s most advanced open database on litter, brands & plastic pollution. The project is open-sourced and would love your contributions, both as users and developers.



Geni Jaho

Full-stack web developer with a passion for software architecture and programming best practices.