Good Code
The good version uses afterCommit() so queued mail reads committed data.
Lesson 09
Dispatch queued work after database commits when jobs depend on rows created inside a transaction.
<?php
use App\Jobs\SendReviewPublishedMail;
use Illuminate\Support\Facades\DB;
final class ReviewPublishingService
{
public function publish(Review $review): void
{
DB::transaction(function () use ($review) {
// The job runs after the review update is committed.
$review->update(['status' => 'published']);
SendReviewPublishedMail::dispatch($review)->afterCommit();
});
}
}<?php
DB::transaction(function () use ($review) {
// The job may read the review before this transaction commits.
$review->update(['status' => 'published']);
SendReviewPublishedMail::dispatch($review);
});The good version uses afterCommit() so queued mail reads committed data.
The bad version dispatches inside the transaction without saying when the worker may run. A fast worker can read stale data or fail to find the row.