Jeffrey Way, the owner of Laracasts, made a pretty good video about this not too long ago.
Events and/or Actions
I am currently working on optimizing the structure of my code. One of the most interesting resources I came across was this well-known article on refactoring to actions. I also recently found this presentation by Adel Faizrakhmanov.
Now, I’m wondering: Should I use actions or events? Or should I combine both—using actions for synchronous tasks and events for asynchronous ones?
What do you think?
@jj15 I just watched it and it answered so many questions that were gathering in my head. I really try to be a better developer, but I think I am currently getting stuck in the "best practice" bubble. The things he is saying just make so much sense. Thanks for that!
@codedoktor You're welcome. I can also get sucked into the same bubble sometimes, always second-guessing myself and wondering if I'm doing it the "right" way. The truth is, there can be many "right" ways to do something. I think one of the hardest things to learn is the ability to determine which way is the most practical for a given project and use case.
@jj15 Amen!
I use events but they can be confusing if you are new to the codebase or have not worked on the project for a while. Unless you know how events are processed it can be tricky to track down where something is being handled.
For many things this is an unnecessary abstraction
@Snapey I have worked with events in the past and I still find them to be too "abstract". Thinking about them I am like "Yeah this makes total sense". But when actually working with them I often find them to be hindering. Let's call it "obstruction through abstraction"
Should I use actions or events?
@codedoktor You should use the appropriate solution for the appropriate problem. Actions and events do not solve the save problem.
- Actions are classes that encapsulate some business logic, i.e. publishing a record.
- Events are for saying something happened in your application, i.e. a record was published.
You should also only use patterns when it’s actually going to make your codebase easier to manage, and not just hastily re-factoring to a particular pattern because you happened to read about it that day.
So, for actions, a good reason to use them is if you have some logic you need to use in more than one spot. For example, if you did have a PublishRecord action, and users were able to publish records from more than one place in your application (i.e. their personal account, or an admin panel); or even in your application’s UI but also via an Artisan command. You could just then re-use your action class where needed:
public function someControllerAction(Record $record, PublishRecordAction $action)
{
$action->handle($record);
}
class PublishRecordCommand extends Command
{
protected $signature = 'record:publish {id}';
public function handle(PublishRecordAction $action)
{
$record = Record::query()->findOrFail($this->argument('id'));
$action->handle($record);
$this->components->info('Record published.');
return 0;
}
}
Events are for communicating something happened. They’re not for encapsulating business logic like an action class is. So you could have a RecordPublished event that you dispatch in your PublishRecordAction class:
class PublishRecordAction
{
public function handle(Record $record)
{
// Mark record as published...
RecordPublished::dispatch($record);
}
}
And then optionally have listeners that react to the record being published, i.e. send any notifications, update any statistics, etc.
@martinbean Thank you for taking the time to write such a comprehensive answer!
First of all, this is not about refactoring in this case. It's a fresh project in which I want to see what I can improve from earlier projects.
Regarding actions: I understand what you are saying, though I am just wondering if there is any way I can combine that with SRP => In this case removing all non-HTTP logic from the controllers. But that would mean I would have an action for basically everything (incl. actions that I might only use in one place). So what do you think is better?
- Having loads of actions eventhough some might only be used at one place
- Only use actions for reuseability reasons and leave the rest of the logic to the controller
@codedoktor Again, try to only extract when it actually makes sense to.
For example, a lot of my controller actions are simple model retrievals or saves:
class ArticleController extends Controller
{
public function store(StoreArticleRequest $request)
{
$article = Article::query()->create($request->validated());
return redirect()->route('article.show', compact('article'));
}
public function show(Article $article)
{
return view('article.show', compact('article'));
}
}
There’s absolutely no benefit of extracting those one-liners to “actions”. I’d only extract to an action if the logic was more complex, or if I needed the functionality of storing an article in multiple places in my application.
Please or to participate in this conversation.