Problem
Several models contain business logic in boot() methods using Eloquent model events (creating, created, deleting). This mixes persistence concerns with domain logic, making it hard to test, override, or skip in tests.
Affected models:
Donor — token generation on creating, logging on created, notifications + logging on deleting
Donation — notifications + token + logging on created, logging on deleting
Proposed Solution
Extract each callback into a dedicated action class, triggered by an event listener bound in EventServiceProvider (or via #[Listen] attributes):
- Dispatch events (or use Eloquent's built-in
eloquent.created: Model etc.)
- Create listeners that delegate to action classes
- Register bindings in
EventServiceProvider
This gives us:
- Testable, single-responsibility actions
- Easy to queue, skip, or swap
- No coupling between model and notification/logging logic
Example
// app/Events/DonationRegisteredEvent.php
final readonly class DonationRegisteredEvent
{
public function __construct(public Donation $donation) {}
}
// app/Listeners/SendDonationNotifications.php
class SendDonationNotifications
{
public function handle(DonationRegisteredEvent $event): void
{
$event->donation->donor->notify(new DonationRegistered(...));
$event->donation->athlete->notify(new AthleteNewDonation(...));
}
}
Scope
Donor model: creating, created, deleting callbacks
Donation model: created, deleting callbacks
- Potentially other models with similar patterns discovered during refactoring
Out of Scope
- Changing the notification content or routes
- Changing the token generation algorithm
Problem
Several models contain business logic in
boot()methods using Eloquent model events (creating,created,deleting). This mixes persistence concerns with domain logic, making it hard to test, override, or skip in tests.Affected models:
Donor— token generation oncreating, logging oncreated, notifications + logging ondeletingDonation— notifications + token + logging oncreated, logging ondeletingProposed Solution
Extract each callback into a dedicated action class, triggered by an event listener bound in
EventServiceProvider(or via#[Listen]attributes):eloquent.created: Modeletc.)EventServiceProviderThis gives us:
Example
Scope
Donormodel:creating,created,deletingcallbacksDonationmodel:created,deletingcallbacksOut of Scope