Ultimate Guide on Sending Email in Laravel

Chris Nwamba
👁️ 228,136 views
💬 comments

Sending emails in web applications has become so essential. Marketing, notifications, newsletters, adverts, etc are some of the reasons why we send emails to our clients. I'd say the majority of websites send automated emails at least via a "Contact us" form.

Let's explore the many possible ways to send emails in a Laravel application.

Choosing An Email Service Provider

Although your first thought when you see "Email Service Provider" may be service providers in Laravel, that is not what I am referring to here. I am referring to online services that provide email sending functionalities via APIs.

Table of Contents

    You might be wondering why you need to make use of a service when you can just go hardcore with SMTP. The old way works fine, no doubt, but if you really want something awesome, robust, scalable and economic, then a service provider is better as it does all the hard jobs and just gives you an endpoint for your program to talk to,

    We are going to review several possible providers and how to set them up in a Laravel application. Speaking of which, install a new Laravel application and leave config/services.php open in your favorite editor.

    Mailgun

    1. Sign up for an account if you have not.
    2. Verify your email and phone number.
    3. You will be redirected to your Dashboard.
    4. Locate your API Key and domain

    Mailtrap

    Mailtrap is awesome for development and testing. It was not built with sending emails in production in mind.

    1. Registier via https://mailtrap.io/register/signup
    2. Verify if necessary
    3. Access your inboxes via https://mailtrap.io/inboxes
    4. Store the SMTP credentials somewhere safe

    Mandrill

    1. Sign up
    2. Setup a domain name
    3. Go to settings from the left menu
    4. Click Add API key

    5. Add Mandrill option in the config/services.php file
    'mandrill' => [
            'secret' => env('MANDRILL_KEY'),
        ],

    There are several more options, including Amazon SES, but we will just focus on a few. They are all very similar to setup so let us just stick with learning with what we have.

    Configuring Our Services

    Our config/services.php has all the configuration for major external services that are required for our application. It is also recommended that if Laravel does not provide any service, you should stick to the design pattern of using the services config file to configure your application.

     'mailgun' => [
            'domain' => env('MAILGUN_DOMAIN'),
            'secret' => env('MAILGUN_SECRET'),
        ],
    
        'mandrill' => [
            'secret' => env('MANDRILL_KEY'),
        ],
    
        'ses' => [
            'key' => env('SES_KEY'),
            'secret' => env('SES_SECRET'),
            'region' => 'us-east-1',
        ],
    
        'sparkpost' => [
            'secret' => env('SPARKPOST_SECRET'),
        ],
    
        'stripe' => [
            'model' => App\User::class,
            'key' => env('STRIPE_KEY'),
            'secret' => env('STRIPE_SECRET'),
        ],

    Notice that Mailtrap is missing here. It uses SMTP and is provided as default in every Laravel installation because it is made for testing purposes.

    A practice really frowned upon is storing credentials in codes as they may leak to the wrong hands, especially while moving a code base around in a VCS. For this reason, Laravel uses a .env file for storing it's credentials and environmental variables:

    APP_ENV=local
    APP_DEBUG=true
    APP_KEY=[APPLICATION_KEY]
    APP_URL=http://localhost
    
    DB_HOST=127.0.0.1
    DB_PORT=3306
    DB_DATABASE=homestead
    DB_USERNAME=homestead
    DB_PASSWORD=secret
    
    CACHE_DRIVER=file
    SESSION_DRIVER=file
    QUEUE_DRIVER=sync
    
    REDIS_HOST=127.0.0.1
    REDIS_PASSWORD=null
    REDIS_PORT=6379
    
    MAIL_DRIVER=smtp
    
    MAIL_HOST=mailtrap.io
    MAIL_PORT=2525
    MAIL_USERNAME=[MAILTRAP_USERNAME]
    MAIL_PASSWORD=[MAILTRAP_PASSWORD]
    MAIL_ENCRYPTION=null
    
    MAILGUN_DOMAIN=[MAILGUN_DOMAIN]
    MAILGUN_SECRET=[MAILGUN_SECRET]
    
    MANDRILL_KEY=[MANDRILL_KEY]

    You can go ahead to replace the usernames, passwords, keys, domains and secrets you got while registering for the email services.

    Setting a Default Option

    As I already mentioned, we cannot use all the services at the same time. In that case, even after setting up multiple providers, we still need to tell Laravel which one to use:

    //SMTP/Mailtrap
    MAIL_DRIVER=smtp
    
    //Mailgun
    MAIL_DRIVER=mailgun
    
    //Mandrill
    MAIL_DRIVER=mandrill
    
    //etc

    Sending Emails

    Setting up the services and configuring them does not actually send the emails, but it is an amazing step we have taken together. We can do better. Let us send an email.

    A Basic Approach

    We will use RESTful approach to send the emails because it is the standard. So get your Postman ready to do some jobs.

    We do not need a Model or a View with where we are headed. Instead we just need a controller and a route. Create a controller named EmailController:

    php artisan make:controller EmailController

    Now add a simple route to app/Http/routes.php:

    Route::post('/send', 'EmailController@send');

    Our controller is pointing to the controller we created and is asking the send action method to process the request. Let us go ahead and create the send action method:

    Note: Action methods are methods in a controller that handle a request.

    public function send(Request $request){
        //Logic will go here        
    }

    That is a basic method waiting and ready to be fleshed out. We have also type-hinted Request if we need anything to do with the request object.

    To send HTML emails, we need to create a template for that. Create a send.blade.php file in resources/views/emails with the following:

    <html>
    <head></head>
    <body style="background: black; color: white">
    <h1>{{$title}}</h1>
    <p>{{$content}}</p>
    </body>
    </html>

    Back to the action method. It is time to actually add the mail sending logic which can be done using the Mail facade:

     public function send(Request $request)
        {
            $title = $request->input('title');
            $content = $request->input('content');
    
            Mail::send('emails.send', ['title' => $title, 'content' => $content], function ($message)
            {
    
                $message->from('me@gmail.com', 'Christian Nwamba');
    
                $message->to('chrisn@scotch.io');
    
            });
    
            return response()->json(['message' => 'Request completed']);
        }

    The Mail Facade which is used to handle emails in Laravel provides several methods including send(). The send() method takes 3 parameters: a blade view, data to be bound to the view, and a closure. You can configure the mail how ever you want in the closure.

    Head to Postman and make a Post request to /send with title and content as seen below:

    Below is an image of this mail as it arrives in Mailtrap

    We can configure all the properties of an email right inside the closure. Below are the available options as provided by Taylor:

    $message->from($address, $name = null);
    $message->sender($address, $name = null);
    $message->to($address, $name = null);
    $message->cc($address, $name = null);
    $message->bcc($address, $name = null);
    $message->replyTo($address, $name = null);
    $message->subject($subject);
    $message->priority($level);
    $message->attach($pathToFile, array $options = []);

    Let us experiment with one more of the features which is $message->attach()

    Attaching Files

    Attaching files is very simple. We just use the attach() method and supply a file path.

     public function send(Request $request)
        {
            $title = $request->input('title');
            $content = $request->input('content');
            //Grab uploaded file
            $attach = $request->file('file');
    
            Mail::send('emails.send', ['title' => $title, 'content' => $content], function ($message) use ($attach)
            {
    
                $message->from('me@gmail.com', 'Christian Nwamba');
    
                $message->to('chrisn@scotch.io');
    
                //Attach file
                $message->attach($attach);
    
                //Add a subject
                $message->subject("Hello from Scotch");
    
            });

    We requested a file uploaded via a form and attached it to the mail. Notice that we also added a subject to the email.

    Optimizing with Queues

    You can now use queues to optimize sending emails. It is dead simple. Instead of sending with the send() method, we send with the queue() method:

     public function send(Request $request)
        {
            //Using queues is better
            Mail::queue('emails.send', ['title' => $title, 'content' => $content], function ($message) use ($attach)
            {
    
            });
        }

    Remember that we have to run the listen command on the queues before they can be dispatched:

    php artisan queue:listen

    In production, it is not advisable to use listen because of it's high CPU usage. It is better we use work and pass the --daemon option:

    sude nohup php artisan queue:work --daemon --tries=3

    Bonus: Bulk Email Notifications with MailChimp

    As this article is one about sending emails, it would be nice to consider sending bulk emails. The popular tool that handles this is Mailchimp so lets try that out.

    Setup MailChimp

    Setting up Mailchimp invovles two steps - account creation and Laravel project setup. To create a MailChimp account, head straight to the Sign up page, sign up, and verify your account if required.

    Next create a list of your subscribers. In a real project, you would be adding users to the list programatically using the API, but we can just go ahead and create some via the dashboard as seen below.

    Now grab the List Id by going to Settings > List name and defaults. Also create and get your API key from User > Accounts > Extras > API Keys. "User" stands for your username, which is on the navigation bar.

    Head back to the Laravel project and store the key and Id in .env:

    MAILCHIMP_KEY=[KEY]
    MAILCHIMP_LIST_ID=[ID]

    Then pull Mandrill's PHP SDK from composer:

    composer require mailchimp/mailchimp

    Sending Campaigns

    Mailchimp's bulk emails are identified as campaigns. Let us create another simple action method to handle sending bulk message to our list of subscribers:

    public function notify(Request $request){
    
            //List ID from .env
            $listId = env('MAILCHIMP_LIST_ID');
    
            //Mailchimp instantiation with Key
            $mailchimp = new \Mailchimp(env('MAILCHIMP_KEY'));
    
            //Create a Campaign $mailchimp->campaigns->create($type, $options, $content)
            $campaign = $mailchimp->campaigns->create('regular', [
                'list_id' => $listId,
                'subject' => 'New Article from Scotch',
                'from_email' => 'pub@gmail.com',
                'from_name' => 'Scotch Pub',
                'to_name' => 'Scotch Subscriber'
    
            ], [
                'html' => $request->input('content'),
                'text' => strip_tags($request->input('content'))
            ]);
    
            //Send campaign
            $mailchimp->campaigns->send($campaign['id']);
    
            return response()->json(['status' => 'Success']);
        }

    We are using two campaign methods: create() and send(). The create() method provisions a campaign and returns an array which contains the id of our campaign. We then pass this id to the send() method to dispatch the emails. That's all!

    Go ahead and add the extra route:

    Route::post('/notify', 'EmailController@notify');

    Conclusion

    If your web application sends emails, it is recommended you take the extra mile and use useful services to optimize the whole process. Test your emails with Mailtrap, send with Mandrill/Mailgun/SES or anything that suites you, and optimize with Queues.

    Chris Nwamba

    109 posts

    JavaScript Preacher. Building the web with the JS community.