Laravel Framework

Notifications & Mail

18 min Lesson 16 of 45

Notifications & Mail in Laravel

Laravel provides a powerful and flexible system for sending notifications across multiple channels including email, SMS, Slack, and database storage. The notification system makes it easy to notify users about events in your application.

Mail Configuration

Before sending mail, configure your mail driver in .env:

# Using SMTP MAIL_MAILER=smtp MAIL_HOST=smtp.gmail.com MAIL_PORT=587 MAIL_USERNAME=your-email@gmail.com MAIL_PASSWORD=your-app-password MAIL_ENCRYPTION=tls MAIL_FROM_ADDRESS=noreply@example.com MAIL_FROM_NAME="${APP_NAME}" # Using Mailgun MAIL_MAILER=mailgun MAILGUN_DOMAIN=your-domain.com MAILGUN_SECRET=your-mailgun-key # Using SendGrid MAIL_MAILER=sendgrid SENDGRID_API_KEY=your-sendgrid-key
Note: For Gmail, you need to use App Passwords (not your regular Gmail password) if 2FA is enabled. Generate one in your Google Account security settings.

Creating Mailables

Mailables represent email messages as PHP classes. Create a mailable using Artisan:

php artisan make:mail WelcomeEmail php artisan make:mail OrderConfirmation --markdown=emails.orders.confirmation

Example mailable class:

<?php namespace App\Mail; use App\Models\User; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; class WelcomeEmail extends Mailable { use Queueable, SerializesModels; public $user; public $verificationUrl; public function __construct(User $user, string $verificationUrl) { $this->user = $user; $this->verificationUrl = $verificationUrl; } public function build() { return $this->subject('Welcome to ' . config('app.name')) ->view('emails.welcome') ->with([ 'userName' => $this->user->name, 'url' => $this->verificationUrl, ]); } }

Sending Mail

Send mail using the Mail facade:

use App\Mail\WelcomeEmail; use Illuminate\Support\Facades\Mail; // Send immediately Mail::to($user->email)->send(new WelcomeEmail($user, $verificationUrl)); // Send to multiple recipients Mail::to($user) ->cc('manager@example.com') ->bcc('admin@example.com') ->send(new OrderConfirmation($order)); // Queue the email for background processing Mail::to($user->email)->queue(new WelcomeEmail($user, $url)); // Queue with delay Mail::to($user->email) ->later(now()->addMinutes(10), new WelcomeEmail($user, $url));

Markdown Mail

Laravel supports beautiful, responsive email templates using Markdown:

// Create markdown mailable php artisan make:mail InvoicePaid --markdown=emails.invoice.paid

Mailable with markdown:

<?php class InvoicePaid extends Mailable { public $invoice; public function build() { return $this->markdown('emails.invoice.paid') ->subject('Invoice Paid - #' . $this->invoice->id); } }

Markdown template (resources/views/emails/invoice/paid.blade.php):

@component('mail::message') # Invoice Paid Thank you for your payment! **Invoice:** #{{ $invoice->id }} **Amount:** ${{ number_format($invoice->amount, 2) }} @component('mail::button', ['url' => route('invoices.show', $invoice)]) View Invoice @endcomponent @component('mail::panel') This invoice has been marked as paid on {{ $invoice->paid_at->format('M d, Y') }}. @endcomponent @component('mail::table') | Item | Quantity | Price | |:-----|:--------:|------:| @foreach($invoice->items as $item) | {{ $item->name }} | {{ $item->quantity }} | ${{ number_format($item->price, 2) }} | @endforeach @endcomponent Thanks,<br> {{ config('app.name') }} @endcomponent
Tip: Customize the markdown email styling by publishing the mail assets: php artisan vendor:publish --tag=laravel-mail

Notifications System

Notifications are more versatile than mail, supporting multiple channels. Create a notification:

php artisan make:notification NewOrderNotification php artisan make:notification PaymentReceived --markdown=notifications.payment-received

Example notification class:

<?php namespace App\Notifications; use App\Models\Order; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Notification; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\BroadcastMessage; class NewOrderNotification extends Notification { use Queueable; public $order; public function __construct(Order $order) { $this->order = $order; } // Specify which channels to use public function via($notifiable) { return ['mail', 'database', 'broadcast']; } // Email notification public function toMail($notifiable) { return (new MailMessage) ->subject('New Order #' . $this->order->id) ->line('You have received a new order.') ->line('Order Total: $' . number_format($this->order->total, 2)) ->action('View Order', route('orders.show', $this->order)) ->line('Thank you for using our application!'); } // Database notification public function toDatabase($notifiable) { return [ 'order_id' => $this->order->id, 'order_total' => $this->order->total, 'customer_name' => $this->order->customer->name, 'message' => 'New order #' . $this->order->id, ]; } // Broadcast notification (for real-time) public function toBroadcast($notifiable) { return new BroadcastMessage([ 'order_id' => $this->order->id, 'message' => 'New order received', ]); } // Array notification public function toArray($notifiable) { return [ 'order_id' => $this->order->id, 'order_total' => $this->order->total, ]; } }

Sending Notifications

Send notifications using the Notifiable trait:

use App\Notifications\NewOrderNotification; // Send to a single user (User model must use Notifiable trait) $user->notify(new NewOrderNotification($order)); // Send to multiple users $admins = User::where('role', 'admin')->get(); Notification::send($admins, new NewOrderNotification($order)); // Anonymous notifiable (send without user model) Notification::route('mail', 'guest@example.com') ->route('slack', config('services.slack.webhook')) ->notify(new NewOrderNotification($order));

Database Notifications

Store notifications in database for in-app notification centers:

// Create notifications table php artisan notifications:table php artisan migrate

Retrieve and mark notifications as read:

// Get all notifications $notifications = $user->notifications; // Get unread notifications $unread = $user->unreadNotifications; // Mark as read $user->unreadNotifications->markAsRead(); // Mark specific notification as read $notification = $user->notifications->find($id); $notification->markAsRead(); // Get notification data foreach ($user->unreadNotifications as $notification) { echo $notification->data['message']; echo $notification->created_at->diffForHumans(); } // Delete notification $notification->delete();

Notification Channels

Laravel supports multiple notification channels out of the box:

// Install Slack notification channel composer require laravel/slack-notification-channel // In notification class public function via($notifiable) { $channels = ['database']; if ($notifiable->email_notifications) { $channels[] = 'mail'; } if ($notifiable->slack_webhook) { $channels[] = 'slack'; } return $channels; } // Slack notification public function toSlack($notifiable) { return (new SlackMessage) ->from('Order Bot', ':shopping_cart:') ->to('#orders') ->content('New order received!') ->attachment(function ($attachment) { $attachment->title('Order #' . $this->order->id) ->fields([ 'Customer' => $this->order->customer->name, 'Total' => '$' . number_format($this->order->total, 2), ]); }); }
Warning: Always queue notifications that send external requests (email, Slack, SMS) to avoid blocking your application. Use the ShouldQueue interface on notification classes.

Queueable Notifications

Make notifications queueable for better performance:

<?php use Illuminate\Contracts\Queue\ShouldQueue; class NewOrderNotification extends Notification implements ShouldQueue { use Queueable; // Specify queue connection public $connection = 'redis'; // Specify queue name public $queue = 'notifications'; // Number of times to attempt public $tries = 3; // Timeout in seconds public $timeout = 60; }
Exercise 1: Create a "PasswordChanged" notification that sends via email and database. The email should use markdown and include a button to contact support if the user didn't make the change.
Exercise 2: Create a notification preference system where users can enable/disable email and database notifications. Modify the notification's via() method to respect these preferences.
Exercise 3: Build a notification center component that displays unread notifications with a badge count. Add functionality to mark notifications as read when clicked and delete them.

Testing Notifications

Test notifications using Laravel's built-in assertions:

use Illuminate\Support\Facades\Notification; public function test_user_receives_welcome_email() { Notification::fake(); $user = User::factory()->create(); $user->notify(new WelcomeEmail($user, 'http://verify.url')); Notification::assertSentTo($user, WelcomeEmail::class); Notification::assertSentTo( $user, WelcomeEmail::class, function ($notification, $channels) use ($user) { return $notification->user->id === $user->id; } ); }

Notifications and mail provide a unified, channel-agnostic way to keep users informed. In the next lesson, we'll explore events and listeners for building decoupled, event-driven applications.