laravellaravel-bladelaravel-mail

Dynamic header and footer in Laravel markdown mail


Following the documentation, I have created my own mail template using markdown mailables: https://laravel.com/docs/9.x/mail#generating-markdown-mailables

The point is that I need to customize information in both the header and footer dynamically (in each situation it will be different). The information I pass in the toMail function is only available in the scope of my custom template neworder.blade.php:

public function toMail($notifiable){
    $from = 'no-reply.'.$this->subdomain.'@'.env('APP_DOMAIN');
    return (new MailMessage)
    ->from($from)
    ->markdown('emails.neworder',
        [
            'name'=>$this->name,
            'order'=> $this->order,
            'emailbody'=> $this->emailbody,
            'headertitle' => $this->headertitle,
            'footertext' => $this->footertext
        ]
    );
}

Following the same documentation, I have exported the Markdown mail components to customize them with this command:

php artisan vendor:publish --tag=laravel-mail

From here I can customize files like /vendor/mail/html/themes/header.blade.php, where the modifications effectively affect the header. What I can't figure out is how to pass variables that I can use in the scope of these files just like in /views/email/neworder.blade.php I need to have the values of headertitle and footertext in the corresponding sections.


Solution

  • You cannot do this with a MailMessage. You need to create a Mailable class and pass it in the toMail() function of your Notification.

    1. Create a new Mailable class.

    2. Make it so that your Mailable class uses your markdown Blade template file.

    3. Pass the data as usual in the markdown() function.

    // app/Mail/NewOrderMail.php
    
    public function build()
    {
        return $this->markdown('emails.new-order', [
            'headerTitle' => 'Header title here',
            'footerText' => 'Footer text here',
        ]);
    }
    
    1. In your Mailable's markdown Blade template file, can pass the data to the layout file like so:
    // resources/views/emails/new-order.blade.php
    
    @component('mail::message', ['headerTitle' => $headerTitle, 'footerText' => $footerText])   
    {{-- ...the rest of your email template... --}}
    @endcomponent
    
    1. In resources/views/vendor/mail/html/message.blade.php, pass the variables to each of the @component that you want to pass the data to:
    // resources/views/vendor/mail/html/message.blade.php
    
    @component('mail::layout')
    
    {{-- Header --}}
    @slot('header')
    @component('mail::header', ['url' => config('app.url'), 'headerTitle' => $headerTitle ?? config('app.name')])
    {{ $headerTitle }}
    @endcomponent
    @endslot
    
    {{-- ...the rest of your layout template... --}}
    
    {{-- Footer --}}
    @slot('footer')
    @component('mail::footer', ['footerText' => $footerText ?? 'Default Text'])
    {{ $footerText }}
    @endcomponent
    @endslot
    
    @endcomponent
    
    1. Return your Mailable class in the toMail() function of your Notification class:
    // app/Notifications/NewOrderNotification.php
    
    public function toMail($notifiable)
    {
        return (new NewOrderMail($notifiable))->to($notifiable);
    }
    

    IMPORTANT: Make sure to null coalesce your variables in your resources/views/vendor/mail/html/message.blade.php layout file to make sure things don't break when an email template file does not pass any of the variables you defined in the layout file.

    All filenames are only used as examples. Replace them with your own.