Never put email sending code in your data model callbacks

This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the architecture category.

Last Updated: 2024-04-26

I wanted to add code to email QR codes after appointments were booked.

I decided to hook it into the book method in the Slot model and then pushed it right to staging.

<?php

public function book(Patient $patient, Ticket $ticket)
{
    // This is quadruple pivot table
    if ($appointment = $this->appointment()->create(
        [
            "product_id" => $ticket->product->id,
            "patient_id" => $patient->id,
            "ticket_id" => $ticket->id
        ]
    )) {
        Mail::to($appointment->user)
            ->queue(new AppointmentQrCode($appointment));

        return $appointment;
    };
    return false;
}

Then, when I reseed the DB in staging, due to the seeder calling model methods (including book), everyone on my team (who has seed user accounts) received emails.

This was a strange and unexpected sort of consequence to seeding. I should have trusted my gut and kept this stuff outside the data-model (despite my client's feelings that he didn't like having too many classes). Tangling up this concerns was plain, old-fashioned bad design.

I ended up moving this functionality to the one controller action it got user. (A service object seemed like overkill)