Refactoring #2: From controllers to actions
Tags: laravel, refactoring, actions, webdev
Published: 2021-12-24 16:36:54

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.
{% gist https://gist.github.com/GeniJaho/e09fb217f55d1be273eb473ab8dc0f96 %}

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

{% gist https://gist.github.com/GeniJaho/db786f0c98391f307076ad56915dd8eb %}

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.

{% gist https://gist.github.com/GeniJaho/0380804be686497517976dba102611f1 %}

If you're wondering about the changed signature, and that `exists` rule, it's [a nice laravel helper](https://laravel.com/docs/8.x/validation#rule-exists).
The final result is a much cleaner controller, where it's easier to understand what's going on.

{% gist https://gist.github.com/GeniJaho/e8f2ca667ad62710673c1afd107f1214
%}

##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:

{% gist https://gist.github.com/GeniJaho/de2458701e7aab9a931e1352da9866da
%}

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](https://freek.dev/1371-refactoring-to-actions) from guys at Spatie.

As an extra, one could also work with [Spatie's Actions](https://github.com/spatie/laravel-queueable-action) package to make them queueable, or even the full-fledged [Laravel Actions](https://laravelactions.com/) package. We kept it simple for now using Plain Old Objects in PHP (POOP).

>The code used for illustration is taken from the [OpenLitterMap](https://github.com/OpenLitterMap/openlittermap-web) 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.