<?php
namespace App\Http\Controllers\Auth;
use App\Events\UserRegistered;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
class RegisteredController extends Controller
{
public function store(Request $request)
{
$data = $request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'unique:users,email'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
$user = User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
event(new UserRegistered($user));
return response()->json([
'message' => 'Registration successful.',
'user' => $user->only('id', 'name', 'email'),
], 201);
}
}
<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class UserRegistered
{
use Dispatchable, SerializesModels;
public User $user;
public function __construct(User $user)
{
$this->user = $user;
}
}
<?php
namespace App\Listeners;
use App\Events\UserRegistered;
use App\Mail\WelcomeMail;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Throwable;
class SendWelcomeEmail implements ShouldQueue
{
public string $connection = 'redis';
public string $queue = 'mail';
public int $tries = 3;
public function backoff(): array
{
return [10, 60, 180];
}
public function handle(UserRegistered $event): void
{
Mail::to($event->user->email)
->send(new WelcomeMail($event->user));
}
public function failed(UserRegistered $event, Throwable $exception): void
{
Log::error('Welcome email permanently failed', [
'user_id' => $event->user->id,
'error' => $exception->getMessage(),
]);
}
}
<?php
namespace App\Providers;
use App\Events\UserRegistered;
use App\Listeners\SendWelcomeEmail;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
UserRegistered::class => [
SendWelcomeEmail::class,
],
];
public function shouldDiscoverEvents(): bool
{
return false;
}
}
This snippet shows the idiomatic Laravel way to send a welcome email after a user registers without blocking the HTTP request: fire a domain event and let a queued listener do the slow work. The pattern decouples the fact that a user registered from the side effects of registration, so the controller stays thin and new reactions (analytics, Slack pings, provisioning) can be added later without touching registration logic.
In RegisteredController, the request is validated and the User is created in a single create call. The controller does not touch the mailer at all — it simply calls event(new UserRegistered($user)) and returns. This keeps the response fast because no SMTP round-trip happens inline, and it means the registration flow has exactly one responsibility.
UserRegistered event is a plain data-carrying class. It uses the Dispatchable and SerializesModels traits: SerializesModels is what makes the event safe to push onto a queue, because it stores only the model's identifier and re-fetches a fresh User when the job runs, avoiding stale or bloated serialized state. The event exposes a public $user so listeners can read it.
SendWelcomeEmail listener is where the interesting part lives. By implementing ShouldQueue, the listener is automatically pushed onto the queue instead of running synchronously — the framework serializes the event and hands it to a worker. The $connection, $queue, and $tries properties tune where and how reliably it runs, and backoff() spaces out retries so a flaky mail provider gets breathing room. The handle() method builds and sends a WelcomeMail mailable to the user. failed() runs only after all retries are exhausted, giving a single place to log the permanent failure.
EventServiceProvider wires the event to its listener through the $listen map, which is how Laravel knows which listeners to invoke for a given event; multiple listeners can be attached to the same event here.
The key trade-off is eventual consistency: the email is sent slightly after the response, and a worker must be running (php artisan queue:work) for delivery to happen. The payoff is a snappy request, automatic retries with backoff, and a registration path that is trivial to extend.
Related snips
require "csv"
class PeopleCsvStream
include Enumerable
HEADERS = %w[id full_name email signed_up_at plan].freeze
Resilient CSV Export as a Streamed Response
<?php
namespace App\Providers;
use App\Contracts\PaymentGateway;
use App\Services\StripePaymentGateway;
Laravel service container and dependency injection
{
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build"
},
Laravel mix/Vite for asset compilation
class CreateTopSellersMv < ActiveRecord::Migration[7.0]
def up
execute <<~SQL
CREATE MATERIALIZED VIEW top_sellers AS
SELECT p.id AS product_id,
p.name AS product_name,
Cache-Friendly “Top N” with Materialized View Refresh
class CreateDeadJobs < ActiveRecord::Migration[7.1]
def change
create_table :dead_jobs do |t|
t.string :jid, null: false
t.string :queue, null: false
t.string :klass, null: false
Background Job Dead Letter Queue (DLQ) Table
// Basic event listener
const button = document.getElementById('myButton');
button.addEventListener('click', function(event) {
console.log('Button clicked!');
console.log('Event type:', event.type);
Event handling and event delegation patterns in JavaScript
Share this code
Here's the card — post it anywhere.