Create a Mailing List in PHP using Laravel, Vue.js, and Twilio's SendGrid Email API

March 18, 2021
Written by
Anumadu Udodiri Moses
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by

Create a Mailing List using Laravel, Vue.js and Twilio's SendGrid Email API

The importance of email marketing cannot be over-emphasized in modern-day business, mostly because of its cost-effectiveness and conversion-to-sales ratio. This tutorial will teach you how to use Twilio’s SendGrid Email API with Laravel and a Vue.js frontend to send bulk emails to more than 100 email addresses at once. This removes the pain of sending out emails slowly, as well as the possibility of emails going to spam.

After we’re finished, you will have developed a custom mailing list that:

  • Allows anyone to enter their email address and save it to the application database
  • Send out bulk emails to provided email addresses

Prerequisites

To follow along with this tutorial you should have the following in place:

Why use the Twilio SendGrid Email API

The Twilio SendGrid library is built to simplify the process of bulk emailing for individuals and businesses. It is available in multiple programming languages and frameworks. The library strives to make the process seamless to send out emails quickly. Let us get started on creating our mailing list.

Let's create our mailing list

Create a new Laravel project using the following Composer command:

$ composer create-project --prefer-dist laravel/laravel twilioSendGrid

You can also use the following command if you have the Laravel installer installed globally in your system:

$ laravel new twilioSendGrid

Regardless of how you create the new Laravel project, once created, switch to the newly created directory using the following command:

cd twilioSendGrid

Configuration and .env setup

Our application will have a small form to collect emails for our mailing list. To save these emails, create a new model, controller, and migration file for our application. Use the command below to create all three at once.

$ php artisan make:model EmailListing -c -m

The previous command should result in output similar to the following being printed to the terminal.

Model created successfully.
Created Migration: 2020_08_13_203124_create_email_listings_table
Controller created successfully.

Now, modify the new migration file database/migrations/2021_02_16_192153_create_email_listings_table.php to add an email field to the up() method as in the following code:

    public function up()
    {
        Schema::create('email_listings', function (Blueprint $table) {
           $table->id();
           $table->string('email');
           $table->timestamps();
        });
    }

Next, in .env, in the root directory of the project, add the configuration details for your local database server. Here is a copy of what my .env database configuration is for this project.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=sendgrid
DB_USERNAME=root
DB_PASSWORD=

Then, run a database migration using the following command:

$ php artisan migrate

Serve the project

To ensure that the code is working as required, start the Laravel application using the following command:

$ php artisan serve

The application should start quite quickly, and you should see the following printed to the terminal:

Laravel development server started: http://127.0.0.1:8000
composer require laravel/ui
php artisan ui vue
npm install && npm run dev

Then, modify the body of the default view template, resources/views/welcome.blade.php, to contain the following code:

...]
    <body>
        <div id="app">
            <app></app>
        </div>
        <script src="{{ mix('js/app.js') }}"></script>
    </body>
[...]

The script tag at the end of the body tag imports js/app.js into our Blade template so that we can use Vue.js in our application. However, we need to make some modifications to it, so that it will work as expected. To do that, modify resources/js/app.js so that it looks like the following example.

require("./bootstrap");

import Vue from "vue";
import App from "./components/App.vue";

const app = new Vue({
   el: "#app",
   components: { App },
});

The code imports Vue.js, and required bootstrap.js and our App.vue file. App.vue holds everything that shows up in the browser. Create a new file named App.vue in the component directory (resources/js/components). Then, add the following code to App.vue:

  <template>
    <div>
        <h1>Hello world</h1>
    </div>
 </template>

With the file updated, use the following command to compile our JavaScript code and watch for any future changes.

$ npm run watch

With the code recompiled, if we reload the browser it should now look like the image below.

The initial output of the application

Set up SendGrid

Next, we need to install the SendGrid SDK for Laravel using the command below.

$ composer require "sendgrid/sendgrid"

After installing the SDK, head over to the SendGrid Dashboard. Then, click Settings -> API Keys. On the API Keys page, click Create API Key in the upper right-hand corner. Enter a name for your new API key. Leave API Key Permissions set to "Full Access", and click Create & View in the lower right-hand corner to create the key.

Create a SendGrid API key

After doing so a new API key will be created. Copy your API key. This is required by the SDK to send emails.

Copy a SendGrid API key
 

Note: Make sure you’ve copied the API key. SendGrid's security measures won't allow you to view it again once you click "Done."

Now add the following configuration to your .env, replacing {YOUR API KEY} with the SendGrid API key you just copied.

SENDGRID_API_KEY={YOUR API KEY}


Before we continue, it's important to know that SendGrid has different levels of verifications for different accounts. If you just created a new SendGrid account or your account is less than 6 months old, SendGrid requires that you verify your sending address. This is the address that your receivers will see as the sender of the email.

If you have not verified your SendGrid sender email address and your account is less than 6 months old, you might encounter a server error asking you to do so when you are trying to send out emails.

Note: To see detailed instructions on how to complete the process, refer to the Single Sender Verification documentation.

With that said, let's continue building our mailing list application.

Seed emails

Our application will store recipient email addresses in a database table called email_listing. To save us some time, we'll seed our email_listing table with email addresses using a Seeder.

Create a new Seeder using the following command:

$ php artisan make:seeder EmailListingSeeder

Open the new Seeder located in database/seeders/EmailListingSeeder.php. Modify it by replacing the existing code with the following:

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;
use Faker\Generator as Faker;
use Illuminate\Support\Facades\DB;

class EmailListingSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run(Faker $faker)
    {
        // Use real emails here
        DB::table("email_listings")->insert([
            [
                'email' => $faker->unique()->safeEmail, 
            ],
            [
                'email' => $faker->unique()->safeEmail, 
            ],
            [
                'email' => $faker->unique()->safeEmail,
            ],
        ]);
    }
}

Note: our seeder class above uses Faker to generate emails. Sending emails to these randomly generated email addresses using SendGrid won't work. I would suggest you ensure that you use legitimate email addresses that can receive emails from the internet.

Run the following command to seed our data.

$  php artisan db:seed --class=EmailListingSeeder 

Send bulk emails

Now that we have emails in the email_listings table of our database, let's add the functionality to send bulk emails. We created a controller earlier EmailListingController. Let's refactor the controller's code to send out emails to email addresses in our database. Replace EmailListingController's existing code with the code below.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\EmailListing;
use SendGrid;

class EmailListingController extends Controller
{
    /**
     * push array in key/value pairs
     */
    protected function array_push_assoc(&$array, $key, $value){
        $array[$key] = $value;
        return $array;
    }

    public function sendEMail(Request $request)
    {
        $validated = $request->validate([
            'emailTopic' => 'required|string',
            'emailBody' => 'required|string',
            'senderEmail' => 'required|email',
        ]);

        $from = $validated['senderEmail'];
        $topic = $validated['emailTopic'];
        $addresses = EmailListing::all();
        $receivers = [];
        $emailContent = $validated['emailBody'];

        foreach($addresses as $address){
            $this->array_push_assoc($receivers, $address->email, 'Example user ');
        }

        $email = new \SendGrid\Mail\Mail();
        $email->setFrom($from, "alloy");
        $email->setSubject($topic);
        $email->addTo("alloyking1@gmail.com", "Example User");
        $email->addTos($receivers);
        $email->addContent("text/plain", $emailContent);
        
        $sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY'));

        try {
            $response = $sendgrid->send($email);
            return response()->json("Email sent successfully");

        } catch (Exception $e) {
            return response()->json( 'Caught exception: '. $e->getMessage() ."\n");
        }
    }
}

Before we continue, let me take a moment to explain the code above. The first method, array_push_assoc(),  was created simply to add values to an array in key/value pairs. This is because SendGrid requires all recipient email addresses to be in a key/value pair. There are many ways you can append values to arrays in PHP but when it comes to key/value pairs, it becomes tricky. This method will come in handy later.

The second method, sendEmail, handles form validation and sending of emails using SendGrid. SendGrid actually has a couple of different ways to send out emails from an application. If you're interested in learning more, they have extensive documentation on how to do so.

Take a close look at the following snippet from the previous code block. This section is solely responsible for interaction with the SendGrid API. 

    $email = new \SendGrid\Mail\Mail();
    $email->setFrom($from, "alloy");
    $email->setSubject($topic);
    $email->addTo("alloyking1@gmail.com", "Example User");
    $email->addTos($receivers);
    $email->addContent("text/plain", $emailContent);
        
    $sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY'));

    try {
         $response = $sendgrid->send($email);
         return response()->json("Email sent successfully");

    } catch (Exception $e) {
         return response()->json( 'Caught exception: '. $e->getMessage() ."\n");
    }

We initialise a new variable, $email as a \SendGrid\Mail\Mail() object. This variable holds a lot of useful information and data about the email sending process.

$email->addTos($receivers);

Now, I want us to pay attention to the snippet above. This is where SendGrid is instructed to send emails out to all the email recipients provided. It receives an argument, $receiver, which is an array of all the recipients' email addresses.

    $addresses = EmailListing::all();
    $receivers = [];
    $emailContent = $validated['emailBody'];

    foreach($addresses as $address){
        $this->array_push_assoc($receivers, $address->email, 'Example user ');
    }

Here, we initialised a new variable, $addresses, with the result of fetching all the recipients' email addresses from our database . Since SendGrid requires these addresses to be passed in a key/value pair, we had to loop through the values of $address to get the email of each receiver. We then added these addresses  to the $receivers array using the method we covered earlier,  $this->array_push_assoc().

Now we should be able to send our emails out. However, we need a route to call the method. To do that, add the following API code to routes/api.php:

Route::post(
    'email/send', 
    [
        \App\Http\Controllers\EmailListingController::class, 
        'sendEMail'
    ]
);

With that done, let's modify resources/js/components/App.vue, which we created earlier for our email testing. Replace the file’s existing code with the following:

<template>
        <div class="container pt-5">
            <br /><br /><br />
            {{ this.success }}
            <div class="card p-5">
                <h4>Email Our Mailing List</h4>
                <small class="pb-3"
                >Sending bulk email to our mailing list contacts using Twilio
                    SendGrid</small
                >
                <form v-on:submit.prevent="send">
                    <div class="form-group">
                        <label>
                            <input
                                type="text"
                                class="form-control"
                                placeholder="Topic"
                                v-model="data.emailTopic"
                            />
                        </label>
                    </div>
                    <div class="form-group">
                        <label>
                            <input
                                type="email"
                                class="form-control"
                                placeholder="Sender Email"
                                v-model="data.senderEmail"
                            />
                        </label>
                    </div>
                    <div class="form-group">
                        <label>
                            <textarea
                                class="form-control"
                                placeholder="Enter email body"
                                v-model="data.emailBody"
                            ></textarea>
                        </label>
                    </div>
                    <button type="submit" class="btn btn-outline-success">
                        Send Emails
                    </button>
                </form>
                <div class="pt-3 danger">
                    {{ this.error }}
                </div>
            </div>
        </div>
</template>

<script>
import axios from "axios";
export default {
        data() {
            return {
                data: {},
                error: "",
                success: ""
            };
        },

        methods: {
            send() {
                if (this.data.emailBody !== "") {
                    axios.post("api/email/send", this.data).then(res => {
                        this.success = "Emails sent successfully";
                    });
                } else {
                    this.error = "This field is required";
                }
            }
        }
};
</script>

We created a basic HTML form in the following code. Nothing too fancy. On submitting the form, the send() Vue.js method is called. We're using it to validate the submitted form fields and make an API call to our email route.

Now, there's one thing left to do; give the form a bit of style. To do that, we're going to add the Bootstrap stylesheet, as the form elements already make use of the Bootstrap classes.

Add the code below at the end of the head section of resources/views/welcome.blade.php.

<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">

Reload the page. All things being equal, your screen should look like mine by now and you should be able to send out bulk emails too. Also, if you use the developer tools, you will see that the body of the response is "Email sent successfully".

The styled form for sending bulk emails.

 

SendGrid uses an email queue. This means that your emails might not be delivered instantly. All things being equal, it should return a 202 response, telling you that your email has been queued to be sent out at a later time. In most cases, these emails are sent out once a day.

That's how to create a mailing list with the Twilio SendGrid Email API, Laravel, and Vue.js

Now that you have completed this tutorial, you know how to:

  • Create and configure a Laravel project with Vue.js as your frontend framework
  • Create a mailing list using Twilio SendGrid email API in Laravel

Now you can ensure that your emails are being delivered to the correct email address. Find a link to the code for this article on GitHub here.

My name is Anumadu Udodiri Moses. I am a software developer and a technical content creator in the PHP and JavaScript ecosystem. I run an online community of tech lovers called Tekiii. You can reach out to me on LinkedIn.