shanechrisbarker.co.uk

Sending Emails with Sendgrid and Laravel - Tutorial

Published On: Tuesday 8th of January 2019 21:37:10

Emails being sent

One of the most common things a website needs to have is a contact form. Usually this will fire off some alert to alert whoever owns the site know that someone just submitted an enquiry.

In this tutorial, we'll use Laravel to build a contact form (the one I have on this site will do). We'll use a form request to validate our input and then finally, we'll use Laravel's built in Mail class to fire off an Email via Sendgrid.

To follow along with this tutorial, you'll need to have Laravel installed (I'm using 5.7). You'll also need to get a Sendgrid account.

Creating the route and the view

To get started, we'll need to make a form for our user to submit. The form will be simple and contain 3 fields:

  • A name
  • A reply email address
  • A message

Before that though, we need to create a route so Laravel knows to show our form when a given URL is called.

Inside of the application, navigate to the routes/web.php file and add the following code:

Route::get('/contact', 'ContactController@index');

This tells Laravel that when someone performs a GET request on our /contact url (such as navigating to it to on our site), we want to call the index action of the ContactController.

But right now, we don't have a ContactController to handle our request - let's make one!

Open up your terminal, navigate to the route of the application and run the following command:

php artisan make:controller ContactController

This will create us our ContactController inside of the app/Http/Controllers directory.

Open up the newly created controller and create the index action we referenced in our routes file earlier. Have this action return a view. This view will hold our contact form.


// app/Http/Controller/ContactController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;

class ContactController extends Controller
{
    public function index()
    {
        return View('contact');
    }
}

The next step is to create the View. Create a new file inside of the resources/views directory named contact.blade.php. Inside of here, we'll create our form:

{{Form::open(['route' => 'post.contact', 'id' => 'contact-form'])}}
{{Form::label('name', 'Please Enter Your Name:', ['class' => 'col-4 form-label'])}}
{{Form::text('name', null, [ 'class' => 'form-control', 'placeholder' => 'E.G Darth Vader', 'required' => 'required' ] )}}
{{Form::label('email', 'Please Enter Your Email Address:', [ 'class' => 'col-4 form-label' ] )}}
{{Form::email('email', null, [ 'class' => 'form-control', 'placeholder' => 'E.G darth.vader@thedarkside.com', 'required' => 'required' ])}}
{{Form::label('message', 'Please Enter Your Message:', ['class' => 'col-4 form-label'])}}
{{Form::textarea('message', null, [ 'class' => 'form-control', 'placeholder' => 'E.G I love you Shane. Here is some chocolate', 'required' => 'required' ])}}
{{Form::submit('submit', ['class' => 'btn btn-primary col-4 offset-4 mb-5'])}}
{{Form::close()}}

If you navigate to /contact on your application now, you'll be met with an error. This is because the route we defined in the opening {{Form::open}} tag doesn't exist yet. As this form will be performing a POST request to our /contact URL, we need to create a route for this.

Open up the routes/web.php file and add a new route to it:

Route::get('/contact', 'ContactController@index');
Route::post('/contact', 'ContactController@makeContact')->name('post.contact');

As you can see. We now have both GET and POST routes for our ContactController. We don't yet have the makeContact() action that the POST method will use, but we will create that soon.

If you now navigate to the /contact URL, you should have a form. For now, we have created the routes and view that we will need. Next we'll move onto validating the form through a Form Request

Creating the Form Request

Obviously any form on a site can be dangerous if we don't properly escape and sanatize the input it takes. On top of this, validating the input is also important.

Laravel provides a great way to handle this via FormRequests. I'm not going to go into them in massive detail, just enough for what we need and to show how they work.

Open up your terminal again at the root of the application and enter the following command:
php artisan make:request ContactFormRequest

This will create us a ContactFormRequest.php file inside of app/Http/Requests - open it up and add the code so it reflects the below:

namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ContactFormRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return  bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return  array
     */
    public function rules()
    {
        return [
            'name'                  => 'required|max:255',
            'email'                 => 'required|max:255',
            'message'               => 'required',
        ];
    }
}

In short, the rules in this file determine what we expect from our input. In this case, simply that all 3 of our fields are required and the message doesn't have a max length. Laravel offers a whole range of rules for validating input so you're bound to find something useful for pretty much any form you build. Check out the docs here

Finishing off our controller

The next step in our email journey is to add the makeContact() function in our ContactController. Remember the one we added to our routes/web.php file earier? Yep, that one.

Head over to the ContactController and add the following code:

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests\ContactFormRequest;
use App\Mail\ContactEmail;
use Illuminate\Http\RedirectResponse;
use Mail;

class ContactController extends Controller
{
    public function index()
    {
        return View('contact');
    }

    public function makeContact(ContactFormRequest $contactFormRequest)
    {
        $data       = $contactFormRequest->input();
        $emailSent  = $this->sendEmail
        (
            $messageText    = $contactFormRequest->message,
            $email          = $contactFormRequest->email,
            $name           = $contactFormRequest->name
        );

        if (true === $emailSent) {
            // mail sent - all good
            return Redirect()->back()->withSuccess
            (
                'Your submission was a success! Whoop! Talk to you soon!'
            );
        }
        // email send failed
        return redirect()->back()->withInput()->withErrors
        (
            "Hmmm... looks like something went wrong there.."
        );
    }

    private function sendEmail($messageText, $email, $name)
    {
        try {
            Mail::to(env('MAIL_TO'))->send(new ContactEmail($messageText, $email, $name));
        } catch (Exception $e) {
            return false;
        }

        return true;
    }
}

Let's break it down:

Our makeContact() function takes an instance of our ContactFormRequest which contain our validated values passed from the form (If the input had failed validation at the ContactFormRequest stage, it wouldn't have made it sas far as this controller).
We call the sendMail() function and based on the return value, we redirect our user back to the previous page along with a relevant message. We'll add the check for and display functionality for these messages to our view shortly.

The sendMail() function itself will fire our email. It takes our validated input and uses Laravels inbuilt Mail() class. It is contained within a try/catch block so if something goes wrong, we can show our friendly message to the user (rather than have our application fall over).

In this instance, the Mail() function is taking a couple of different parameters.

  • The Mail::To() function is taking a string which contains the email address we'll be sending the email to. I've contained this within Laravels .env file so go ahead and open it up and add the following value to it:
    MAIL_TO=yourEmail@yourSite
  • The send() function of the Mail() class takes instance of a new ContactEmail(). This in turn takes our validated paramaters. We'll create the ContactEmail() class now.
Now open up your resources/contact.blade.php file and let's add the code that will show the success/error messages we return to our user after the form is submitted.

Right above the{{Form::Open}} tag, add the following code:
@if(session('success'))
    
{{session('success')}}
@endif @if($errors->any())
{{$errors->first()}}
@endif

Creating the email view and class

We'll now create our ContactEmail() class now - go to your terminal, navigate to the root of the app and run the following command:

php artisan make:mail ContactEmail
This will create a ContactEmail.php file located at app/Http/mail. Open the file up and add the following:

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;

class ContactEmail extends Mailable
{
    use Queueable, SerializesModels;
    private $messageText;
    private $email;
    private $name;

    /**
     * Create a new message instance.
     *
     * @return  void
     */
    public function __construct($messageText, $email, $name)
    {
        $this->name         = $name;
        $this->email        = $email;
        $this->messageText  = $messageText;
    }

    /**
     * Build the message.
     *
     * @return  $this
     */
    public function build()
    {
        return $this->View('email')
            ->from('contact@shanechrisbarker.co.uk', 'Shane Barker')
            ->subject('Hi Shane! Someone sent you a message')
            ->with('email', $this->email)
            ->with('name', $this->name)
            ->with('messageText', $this->messageText);
    }
}

Looking at the bits we've added, we can see:

  • We set some private variables which our __construct() sets - These are the variables we passed when we instantiated our ContactEmail in the ContactController previously.
  • We have our build() function which returns a view. It sets our from, to and email subject fields and then passes our variables into it.
We don't have that view yet - so time to create it.

Inside of resources/views, create a new file called email.blade.php. Inside of it, add the following code:

Enquiry By: {!! $name !!}


Message: {!! $messageText !!}


Reply to: {!! $email !!}

Obviously our email isn't going to look amazing but that's not what this tutorial is about. If you want to style your email, use inline CSS and do some research as emails and email clients can be a nightmare.

Right! We now have one final step before our email will send. We need to add our SendGrid credentials and info to our .env file. Open it up and add the following code:

MAIL_DRIVER=smtp
MAIL_HOST=smtp.sendgrid.net
MAIL_PORT=587
MAIL_USERNAME=your_sendgrid_username
MAIL_PASSWORD=your_sendgrid_password
MAIL_ENCRYPTION=tls

And with that, we're done! Fill in and submit your form and Laravel will call Sendgrid and fire off the email! - How easy was that?

Our finished email should look something like the image below:

Our Finished Email!