How to customize the email verification email from Laravel 5.7?
Asked Answered
B

10

29

I just upgraded to Laravel 5.7 and now I am using the built in Email Verification. However there is 2 things I have not been able to figure out and the primary issue is how can I customize the email that is being sent to the user for verifying their email? I also can't figure out how to initiate sending that email if the users changes their email but I can save that for another thread.

Brimful answered 20/9, 2018 at 2:42 Comment(0)
A
43

When you want to add Email Verification in Laravel 5.7 the suggested method is to implement Illuminate\Contracts\Auth\MustVerifyEmail and use the Illuminate\Auth\MustVerifyEmail trait on the App\User Model.

To make some custom behaviour you can override the method sendEmailVerificationNotification which is the method that notifies the created user by calling the method notify, and passes as a parameter a new instance of the Notifications\MustVerifyEmail class.

You can create a custom Notification which will be passed as a parameter to the $this->notify() within the sendEmailVerificationNotification method in your User Model:

public function sendEmailVerificationNotification()
{
    $this->notify(new App\Notifications\CustomVerifyEmail);
}

...then in your CustomVerifyEmail Notification you can define the way the verification will be handled. You can notify created user by sending an email with a custom verification.route which will take any parameters that you want.

Email verification notification process

When a new user signs-up an Illuminate\Auth\Events\Registered Event is emitted in the App\Http\Controllers\Auth\RegisterController and that Registered event has a listener called Illuminate\Auth\Listeners\SendEmailVerificationNotification which is registered in the App\Providers\EventServiceProvider:

protected $listen = [
    Registered::class => [
        SendEmailVerificationNotification::class,
    ]
];

The SendEmailVerificationNotification listener checks if the $user – which is passed as a parameter to new Registered($user = $this->create($request->all())) in the Laravel default authentication App\Http\Controllers\Auth\RegisterController – is an instance of Illuminate\Contracts\Auth\MustVerifyEmail which is the name of the trait that Laravel suggests is used in the App\User Model when you want to provide default email verification and also check that $user is not already verified. If all that passes, the sendEmailVerificationNotification method is called on that user:

if ($event->user instanceof MustVerifyEmail && !$event->user->hasVerifiedEmail())   {
    $event->user->sendEmailVerificationNotification();
}
Ambassadress answered 25/9, 2018 at 10:9 Comment(0)
S
20

I think the simple way to do this is to make a new notification using the docs here: https://laravel.com/docs/5.7/notifications#creating-notifications

Then override the function:

public function sendEmailVerificationNotification()
{
    $this->notify(new App\Notifications\CustomEmailNotification);
}

In the users model.

Or you can

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

This will copy the templates to the resources/views/vendor/notifications directory and you can modify them there

Sharilyn answered 6/12, 2018 at 19:26 Comment(3)
In my case (customizing the default verify email of laravel 5.8), I had to combine this answer with Mere Development's answer and do some extra works to get the job done.Disqualify
Also, take a look at Illuminate\Auth\Notifications\VerifyEmail & compare it with the CustomEmailNotification. It is useful to see laravel.com/docs/5.8/mail#customizing-the-components and laravel.com/docs/5.8/notifications too. and one last thing, I don't know why CSS theming didn't work for me, what I did was to just copy all the styles that I needed directly into the vendor/mail/html/layout.blade.php file.Disqualify
In addition to this answer, need to add this reference: medium.com/cs-code/laravel-email-verification-apis-9c9e8a46ab03Vellicate
S
7

Unfortunately this email that is sent out is not from a "view", it is a Notification that is built inline actually. This is where it is currently built when needing to be sent off: Illuminate\Auth\Notifications\VerifyEmail@toMail. This particular class has a static callback that can be set to build this email instead of letting it do it.

In a Service Provider in the boot method you will need to assign a callback for this class:

Something "like" this might work:

public function boot()
{
    \Illuminate\Auth\Notifications\VerifyEmail::toMailUsing(function ($notifiable) {

        // this is what is currently being done
        // adjust for your needs

        return (new \Illuminate\Notifications\Messages\MailMessage)
            ->subject(\Lang::getFromJson('Verify Email Address'))
            ->line(\Lang::getFromJson('Please click the button below to verify your email address.'))
            ->action(
                \Lang::getFromJson('Verify Email Address'),
                $this->verificationUrl($notifiable)
            )
            ->line(\Lang::getFromJson('If you did not create an account, no further action is required.'));

    });
}

As this is a notification you should have more options on customizing it.

If you want to use your own Notification class you can override the sendEmailVerificationNotification method on the User (Authenticatable) model (this is from the MustVerifyEmail trait).

Second Question:

The VerificationController (App\Http\Controllers\Auth\VerificationController) that you should have has a method named resend (from the trait VerifiesEmails) that looks like a good candidate for this purpose.

You should have routes setup for these verification routes via Auth::routes(['verify' => true]);

Note:

The verification system uses a field on the users table email_verified_at in 5.7 to mark this. You would want to make sure you have this field. When the user changes email address I suppose you could make this null then redirect them to the resend route, to send off the new verification. This will put them into an "unverified" state though until they reverify, if that is what you intend to happen.

Update:

Seems we were going down the right track. I found this SO answer that goes over similar things:

Changing the default “subject” field for the verification email in laravel 5.7

Sunglasses answered 20/9, 2018 at 3:41 Comment(2)
Thanks for your help. You definitely got me on the right track. I have not yet implemented question/answer 1 yet but for question 2 it is very simple just call a method on the User model now. $user->sendEmailVerificationNotification(); I am working on question 1 and will post results.Brimful
I thinks it's the best answer because you pointed me to a more rapid solutionRevivalism
P
7

I'll show you how to customize User verification email using custom view from scratch without using any vendor publish

Step: 1

Create a new notification UserVerifyNotification class. It should extend the VerifyEmail class from the library Illuminate\Auth\Notifications\VerifyEmail;

Code :

    use Illuminate\Auth\Notifications\VerifyEmail;
    ...
    class UserVerifyNotification extends VerifyEmail implements ShouldQueue
    {
        use Queueable;
        public $user;            //you'll need this to address the user
    
        /**
         * Create a new notification instance.
         *
         * @return void
         */
        public function __construct($user='')
        {
            $this->user =  $user ?: Auth::user();         //if user is not supplied, get from session
        }
    
        /**
         * Get the notification's delivery channels.
         *
         * @param  mixed  $notifiable
         * @return array
         */
        public function via($notifiable)
        {
            return ['mail'];
        }
    
        /**
         * Get the mail representation of the notification.
         *
         * @param  mixed  $notifiable
         * @return \Illuminate\Notifications\Messages\MailMessage
         */
        public function toMail($notifiable)
        {
            $actionUrl  = $this->verificationUrl($notifiable);     //verificationUrl required for the verification link
            $actionText  = 'Click here to verify your email';
            return (new MailMessage)->subject('Verify your account')->view(
                'emails.user-verify',
                [
                    'user'=> $this->user,
                    'actionText' => $actionText,
                    'actionUrl' => $actionUrl,
                ]);
        }
    
        /**
         * Get the array representation of the notification.
         *
         * @param  mixed  $notifiable
         * @return array
         */
        public function toArray($notifiable)
        {
            return [
                //
            ];
        }
        
    }
    

    

Step:2

Create the blade view for the email (user-verify.blade.php) inside resources\views\emails

    <DOCTYPE html>
    <html lang="en-US">
         
        <head>
            <meta charset="utf-8">
        </head>
    
        <body>
            <p>Dear {{$user->name}},</p>
            <p>
                Please click the button below to verify your email address.
            </p>
    
            
            <a href="{{ $actionUrl }}" class="button">{{$actionText}}</a>
            
            <p>If you did not create an account, no further action is required.</p>
    
            <p>
                Best regards, <br>
               
                {{ config('app.name')}}
            </p>
    
            <p>
                <hr>
                <span class="break-all">
                <strong>If you’re having trouble clicking the link, copy and paste the URL below into your web browser:</strong><br/>
                <em>{{$actionUrl}}</em>
            </p>
    
            
    
        </body>
    
    </html>

Step:3

Add the following method inside the User model

class User extends Authenticatable implements MustVerifyEmail
   {
    use HasFactory, Notifiable;

    ...
    ...
    ...

    public function sendEmailVerificationNotification()
    {
        $this->notify(new \App\Notifications\UserVerifyNotification(Auth::user()));  //pass the currently logged in user to the notification class
    }

}

Explanation

  1. When a new user is registered, the Registered event (Illuminate\Auth\Events) is called.
  2. There is a listener inside EventServiceProvider (App\Providers\EventServiceProvider) that listens to the Registered event. 1. Once registration is completed, it calls SendEmailVerificationNotification method (Illuminate\Auth\Listeners\SendEmailVerificationNotification).
  3. The listener (step-2) calls the sendEmailVerificationNotification() method from the library Illuminate\Auth\Listeners\SendEmailVerificationNotification
  4. We now override the sendEmailVerificationNotification() function in Step: 3 and indicate that we would like to use our own notification class which we made earlier in Step: 1
  5. The notification class gets the 'verification link' and calls the 'user-verify' blade to send email with the necessary link as defined in Step: 2
  6. You'll need to add routes required for verification. Just add the following route in the web.php file
    Auth::routes([
        'verify' => true,
        'register' => true,
    ]);
Physicalism answered 25/12, 2020 at 7:41 Comment(1)
Thak you Bruv! Você é demaisss!Exaggeration
G
6

For quick and easy way:

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

It's creating a new file in:

\resources\views\vendor\notifications

This is Laravel's email themplate. You can change and customize it.

Galan answered 30/12, 2018 at 22:13 Comment(0)
C
2

Building slightly on the answer by Andrew Earls, you can also publish all the markdown mail components used by the application with this command:

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

Once that's done you'll have a series of html and markdown files to modify in resources/views/vendor/mail. This will allow you to modify the overall email layout and also 'theme' the CSS. I'd highly recommend having a good read of the Mail docs - Customizing The Components.

CSS theming

As a general email theming quick-start (Laravel 5.7), you can:

  1. Publish the theme with php artisan vendor:publish --tag=laravel-mail .
  2. Copy resources/views/vendor/mail/html/themes/default.css to your own file. e.g resources/views/vendor/mail/html/themes/wayne.css
  3. Edit config/mail.php and where you see 'theme' => 'default' change it to 'theme' => 'wayne'
  4. Edit wayne.css to restyle your emails.

Hope that helps someone.

Congress answered 19/12, 2018 at 17:11 Comment(0)
P
2

Laravel has internal templates for mails and notifications. Notification templates belongs to mail templates. So notification template is a message component of mail. Remember this.

To manage/edit/customize Laravel mail templates you have to publish it. Just run this commands:

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

Laravel will put these files to:

/resources/views/vendor/mail
/resources/views/vendor/notifications

So now you can take your own mail HTML template and integrate it to /resources/views/vendor/mail/html:

  1. layout.blade.php - base layout
  2. header.blade.php - header
  3. footer.blade.php - footer
  4. button.blade.php - button/action
  5. message.blade.php - email body (notification text)

Notifications has just one file at /resources/views/vendor/notifications it's email.blade.php. Usually, you don't need to modify it.

Important thing. Do not add indents/tabs (as in usual HTML files) to these blade files. That will transform HTML to a plain text (don't ask why).

Let's take a look for an example

Laravel user verify email notification with a custom verify URL/link that follows user UUID instead of internal ID. Official docs.

Make an own notification class via this command:

php artisan make:notification VerifyEmail

It will add VerifyEmail.php to app/Notifications folder. Let's update it:

<?php

namespace App\Notifications;

use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\URL;

class VerifyEmail extends \Illuminate\Auth\Notifications\VerifyEmail
{
    protected function buildMailMessage($url)
    {
        return (new MailMessage)
            ->subject('Super subject')
            ->line('some text line 1')
            ->action('verify!', $url)
            ->line('some text line 2');
    }

    //$notifiable = User model object
    protected function verificationUrl($notifiable)
    {
        if (static::$createUrlCallback) {
            return call_user_func(static::$createUrlCallback, $notifiable);
        }

        return URL::temporarySignedRoute(
            'verification.verify',
            Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
            [
                'uuid' => $notifiable->uuid,//custom User model field
                'hash' => sha1($notifiable->getEmailForVerification())
            ]
        );
    }
}

Edit User model /app/Models/User.php:

  1. Model has to implement \Illuminate\Contracts\Auth\MustVerifyEmail interface
  2. Model has to use trait Illuminate\Notifications\Notifiable
  3. You have to override sendEmailVerificationNotification() method with a custom notification class in use

User model class code:

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable implements MustVerifyEmail
{
    use Notifiable;

    /*some code*/

    public function sendEmailVerificationNotification()
    {
        $this->notify(new \App\Notifications\VerifyEmail);
    }
}

Also in that case you have to modify verification.verify route at /routes/web.php. Something like that.

use Illuminate\Foundation\Auth\EmailVerificationRequest;
// {uuid} instead of {id}
Route::get('/email/verify/{uuid}/{hash}', function (EmailVerificationRequest $request) {
    $request->fulfill();
    return redirect('/home');
})->middleware(['auth', 'signed'])->name('verification.verify');
Paleogeography answered 9/3, 2023 at 17:4 Comment(0)
G
0

To send verification email you can just use the next code:

 // send new verification email to user
 $user->sendEmailVerificationNotification();
Grandiloquent answered 25/9, 2019 at 16:19 Comment(0)
H
0

In Route File

Auth::routes(['verify' => true]);

In AppServiceProvider.php File

namespace App\Providers;
use App\Mail\EmailVerification;
use Illuminate\Support\ServiceProvider;
use View;
use URL;
use Carbon\Carbon;
use Config;
use Illuminate\Auth\Notifications\VerifyEmail;
use Illuminate\Notifications\Messages\MailMessage;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        // Override the email notification for verifying email
        VerifyEmail::toMailUsing(function ($notifiable){        
            $verifyUrl = URL::temporarySignedRoute('verification.verify',
            \Illuminate\Support\Carbon::now()->addMinutes(\Illuminate\Support\Facades 
            \Config::get('auth.verification.expire', 60)),
            [
                'id' => $notifiable->getKey(),
                'hash' => sha1($notifiable->getEmailForVerification()),
            ]
        );
        return new EmailVerification($verifyUrl, $notifiable);

        });

    }
}

Now Create EmailVerification With Markdown

php artisan make:mail EmailVerification --markdown=emails.verify-email

Edit The EmailVerrification as you want and the blade file

class EmailVerification extends Mailable
{
    use Queueable, SerializesModels;
    public $verifyUrl;
    protected $user;
    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($url,$user)
    {
        $this->verifyUrl = $url;
        $this->user = $user;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        $address = '[email protected]';
        $name = 'Name';
        $subject = 'verify Email';
        return $this->to($this->user)->subject($subject)->from($address, $name)->
        markdown('emails.verify',['url' => $this->verifyUrl,'user' => $this->user]);
    }
}

in the blade file change the design as you want and use verifyUrl to display the verification link and $user to display user information

thanks, happy coding :)

Henke answered 23/9, 2020 at 23:17 Comment(0)
V
-27

Navigate to these files

  • vendor/laravel/framework/src/Illuminate/Auth/MustVerifyEmail.php

  • vendor/laravel/framework/src/Illuminate/Auth/Notifications/VerifyEmail.php

and then customize it. you can even introduce a constructor in vendor/laravel/framework/src/Illuminate/Auth/Notifications/VerifyEmail.php and pass value through vendor/laravel/framework/src/Illuminate/Auth/MustVerifyEmail.php

eg: Created my own constructor Utilized the user array values passed to the constructor Passing the constructor value from the

Vladamir answered 23/2, 2019 at 2:2 Comment(5)
Never ever edit vendor directory files, biggest mistake in the world.Ikkela
Editing files inside the vendor directory is not a good idea because the files are over-written once you update your dependencies via composer update.Nelly
Vendor will change, when you update with composer. Never try to edit files on vendor.Deciare
The interface MustVerifyEmail interface allows you to override the functionality that is specified there. So that you don't have to edit any /vendor/ files. Instead, copy any method you like from that interface into the User model and then copy the vendor files you need and paste them inside that function in the User model ...Johan
You should not edit the vendor directory files, as it'll create issues when you actually update your dependencies.Marigolde

© 2022 - 2024 — McMap. All rights reserved.