12. Laravel Actions. Fat Models or Fat Controllers? Service Layer?

Video version
(Leave your feedback on YouTube)

USE Actions. For Laravel - Laravel action package

Laravel action is absolutely the best solution I have worked with.

The documentation is comprehensive, but here are a few tips from my personal experience:

  • "Cherry picking" - allows you to limit an Action to only the set of tools that are used. This is, of course, a micro-optimization, but it makes project maintenance easier. The developer will have a better understanding of how an Action is used. For stub Actions, I recommend setting the "AsController" trait by default - it will be encountered most frequently.
  • Do not use public static function routes(Router $router). Unfortunately, it is not cached. route:cache does not work. And since it is static, Laravel Octane does not work well with it.
  • Use handler as the base function. Write all differences for each usage variant in specific functions (asController, asJob, etc.).
  • $this->handler should be called from all specific functions. If this is not the case, you most likely have crammed 2 Actions into one.
  • run is a convenient function but does not allow the use of PHP8 named parameters. SomeAction::make()->handler() will make navigation a bit easier.
  • runIf, dispatchIf, runUnless, dispatchUnless - reduce the cognitive complexity of the code.
  • Laravel Actions are well tested. A small tip - when an Action is simple, a feature test is sufficient. When it is complex, test only access and response formats in feature tests, and test the ActionHandler itself with unit tests.
  • AsFake is very helpful when testing ActionAsObject. Test ActionAsObject separately and simply call its mock in the Actions where it is used. Check shouldRun, shouldNotRun. Check input parameters with shouldRun->with. And mock the result with shouldRun->andReturn.
  • It is better to move validation to FormRequest. This will reduce code and allow for proper reuse of FormRequest.
  • The WithAttributes trait makes the code more complex. It is better to fully utilize PHP8 named parameters. And, of course, running validation on parameters that we set ourselves in the method is not very productive.
  • One task - one Action. Do not create extra layers of code, do not use extra patterns, do not create extra services. An Action will cover the vast majority of your requests.
  • Action is ideal for interaction between modules. In the case of moving a module to a service, an Action will simply start calling the code from the service.