Actions in Laravel beyond CRUD

In the past couple of months, I’ve been enjoying reading Brent’s writing on PHP & Laravel. The test and types article is one of my favorites, as it highlights the importance of types and creating objects that reflect precise concepts, rather than just using primitives for everything.

Recently he’s been writing a mini-series titled “Laravel beyond CRUD” where he explains how he and his colleagues at Spatie structure projects using a DDD (domain-driven design) approach.

The whole series is excellent, and I encourage you to read it. What I liked best is the concept of Actions. A simple class with a single public method that takes input, does something with it, and returns the output.

The main win you get out of using Actions is that you now have a dedicated place to put business logic, instead of just shoving everything in your eloquent models. It relieves you from feeling the shame of having methods doing too much.

A complete() method on an Order class might:

  • update some order fields in the database,
  • create an invoice,
  • recalculate the product’s stock,
  • send a few e-mails,
  • and dispatch events

With Actions, all of this goes into a CompleteOrderAction .

You might choose to keep everything there, or split it even further into multiple actions to allow for reusability: CreateInvoiceAction, SendOrderEmailsAction, TriggerOrderEventsAction.

Another thing I liked about Actions is that it encourages you to think more in terms of processes and less in programming “what fields should be updated” terms. You transition from “what method(s) should be called” to “what action should be performed”. And that’s powerful and clear.

Example. I’m working on an app for car services where the front desk (consultant) creates a “Job” and assigns it to a mechanic. The mechanic then starts inspecting the car and uploads videos&photos with everything he finds wrong. At some point, there’s a step in which he needs to submit his final observations.

One might be tempted to create a JobObservationsController@update where the observations field is being updated. But then you also need to notify the client and the consultant, and you either end up putting everything in the controller or in a updateObservations method in the Job model.

Creating an action makes it clear that UpdateJobObservations is an actual process representing a real-life action performed by your app users, rather than just a bunch of fields being updated.

Changing Laravel’s default directory

The structure looks great on paper. You no longer have just a few directories holding twenty-thirty files. Every domain is organized nicely under its own directory and subdirectories. But.

While Spatie is a leading voice in the Laravel community, I am skeptical about changing the default structure of my Laravel projects mainly because I’ve tried this in the past and was burned really, really bad by it (as many others have). And also, from what I understand, this approach is still quite new at Spatie. I might make the jump on my next project, though.

Actions are great. Use them. And read Brent’s “Laravel beyond CRUD” series. It’s great!

Photo by Sven Mieke on Unsplash

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.