Laravel 5.1 Beauty - Sending Mail and Using Queues

Adding a "Contact Us" page and sending any contacts via queued email.

Posted on June 7, 2015 in L5 Beauty
Note: this is step twelve of the tutorial.

In this chapter we’ll add a Contact Us form to the blog. To do this we’ll explore Laravel’s mailing functions and set up a queue for asynchronous processing.

Contents

Setting Up for Emails

In order to use Laravel 5.1’s mail functionality it first needs to be configured. Configuration is easy. Look at the mail settings that come out of the box in the .env file.

Mail Configuration in .env

MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null

As you can see, just a few simple settings.

Configuring for Gmail

Let’s say you have a gmail account you wish to configure. Here’s how to do it.

First, edit config/mail.php as instructed below.

Changes to config/mail.php

// Find the following line
  'from' => ['address' => null, 'name' => null],
// Change it to
  'from' => ['address' => env('MAIL_FROM'), 'name' => env('MAIL_NAME')],

This sets up who the emails are from which is required by Gmail and is good practice for others.

Next, edit .env, changing the mail configuration to what’s below (replacing USERNAME, PASSWORD, FROM, etc. your own settings).

Gmail Configuration in .env

MAIL_DRIVER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=YOUR@EMAIL.COM
MAIL_PASSWORD=YOUR-GMAIL-PASSWORD
MAIL_FROM=YOUR@EMAIL.COM
MAIL_NAME=YOUR-NAME

Are You Using 2-Step Authentication

If your Gmail account uses 2-Step Authentication then the password required will not be the same one you log into Gmail with. You’ll need to set up an App Password. See Google Help for instructions on how to do this.

The section after the next one (Testing Mail with Tinker) will explain how you can test your mail configuration.

Configuring for Mailgun

Another popular option is to use Mailgun to send your email. I use Mailgun. It’s completely free for the first 10,000 emails you send each month. After that it’s a penny for every 20 emails.

To configure to send through mailgun, first edit config/services.php to match what’s below.

Mailgun Settings in config/services.php

  'mailgun' => [
    'domain' => env('MAILGUN_DOMAIN'),
    'secret' => env('MAILGUN_SECRET'),
  ],

Yes, we’re just setting it up to read the values from the .env file.

Next, Mailgun requires the Guzzle Http library, so use composer to require it.

Requiring Guzzle Http

~/Code/l5beauty% composer require "guzzlehttp/guzzle=~5.0"
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing react/promise (v2.2.0)
    Loading from cache

  - Installing guzzlehttp/streams (3.0.0)
    Loading from cache

  - Installing guzzlehttp/ringphp (1.1.0)
    Loading from cache

  - Installing guzzlehttp/guzzle (5.3.0)
    Loading from cache

Writing lock file
Generating autoload files
Generating optimized class loader

Finally, edit .env and change the settings for Mailgun.

Mailgun Configuration in .env

MAIL_DRIVER=mailgun
MAIL_FROM=YOUR@EMAIL.COM
MAIL_NAME=YOUR-NAME
MAILGUN_DOMAIN=THE-DOMAIN-SETUP-IN-MAILGUN
MAILGUN_SECRET=THE-API-KEY-FOR-DOMAIN

The nice thing about Mailgun is that email is sent via an API, which is faster than using SMTP.

Testing Mail with Tinker

Laravel’s emailer always uses views to send the email. So let’s first create a simple test view.

Create the resources/views/emails directory and within it create the test.blade.php file with the following content.

Content of emails.test view

<p>
  This is a test, an email test.
</p>
<p>
  The variable <code>$testVar</code> contains the value:
</p>
<ul>
  <li><strong>{{ $testVar }}</strong></li>
</ul>
<hr>
<p>
  That is all.
</p>

Now fire up the artisan tinker command and send an email to yourself as instructed below.

Testing Email with Tinker

~/Code/l5beauty$ php artisan tinker
Psy Shell v0.4.4 (PHP 5.6.2 — cli) by Justin Hileman
>>> Mail::send('emails.test',
... ['testVar' => 'Just a silly test'],
... function($message) {
...   $message->to('YOUR@EMAIL.com')
...           ->subject('A simple test');
... });
=> 1
>>> exit

The first argument is the view. The second is an array of any variables the view requires (and emails.test requires testVar). The third is a closure to do additional processing on the message. Here we just set the to address and the subject line.

You can do many things in this closure. Here’s just a few.

  • ->from($address, $name = null) - Add a from address to the message.
  • ->sender($address, $name = null) - Set the sender of the message.
  • ->to($address, $name = null) - Add a recipient to the message.
  • ->cc($address, $name = null) - Add a carbon copy recipient to the message.
  • ->bcc($address, $name = null) - Add a blind carbon copy.
  • ->replyTo($address, $name = null) - Add a reply-to recipient.
  • ->subject($subject) - Set the subject of the message.
  • ->attach($file, array $options = []) - Attach a file to the message.

The above tinker example used a Gmail configuration, which returns 1 indicating success. If you use the Mailgun driver in your configuration, a successful return value will look different.

Testing Email with Tinker (Mailgun config)

~/Code/l5beauty$ php artisan tinker
Psy Shell v0.4.4 (PHP 5.6.2 — cli) by Justin Hileman
>>> Mail::send('emails.test',
... ['testVar' => 'Just a silly test'],
... function($message) {
...   $message->to('YOUR@EMAIL.com')
...           ->subject('A simple test');
... });
=> <GuzzleHttp\Message\Response #0000000024da8555000000017f74f2c8> {}
>>> exit

Adding a Contact Us Form

Now that we know Laravel’s mailer will work, let’s create a form to email us contact information from the user.

The link to the contact form should appear on every blog page. Edit the navbar partials view as below.

Changes to partials.navbar view

// Change the following area
  {{-- Collect the nav links, forms, and other content for toggling --}}
  <div class="collapse navbar-collapse" id="navbar-main">
    <ul class="nav navbar-nav">
      <li>
        <a href="/">Home</a>
      </li>
    </ul>
  </div>

// To the following
  {{-- Collect the nav links, forms, and other content for toggling --}}
  <div class="collapse navbar-collapse" id="navbar-main">
    <ul class="nav navbar-nav">
      <li>
        <a href="/">Home</a>
      </li>
    </ul>
    <ul class="nav navbar-nav navbar-right">
      <li>
        <a href="/contact">Contact</a>
      </li>
    </ul>
  </div>

Easy, just a link which we’ll now create a route for.

Changes to routes.php

// After the following line
get('blog/{slug}', 'BlogController@showPost');

// Add these two lines
$router->get('contact', 'ContactController@showForm');
Route::post('contact', 'ContactController@sendContactInfo');

Notice we used $router directly instead of the get() function. Then instead of the post() function, the Route facade was used. This is simply to illustrate there’s multiple ways to set up the routes. Normally, I just use the helper functions directly, but some people prefer the $router variable or the Route facade.

Routing Shortcut Functions

Laravel 5.1 also provides the following shortcut functions for routing. Any of these can be used directly on the $router variable or with the Route facade.

  • delete($uri, $action) registers a new DELETE route.
  • get($uri, $action) registers a new GET route.
  • patch($uri, $aciton) registers a new PATCH route.
  • post($uri, $action) registers a new POST route.
  • put($uri, $action) registers a new PUT route.
  • resource($name, $controller, $options) registers a resource controller.

When you group routes together, there’s no short helper function so either Route::group() or $router->group() must be used.

Creating the FormRequest

We know the contact form will contain a name, email address, and a message. The Laravel “way” to validate forms is through FormRequest objects which we used quite a bit in the administration area of our blog.

Let’s create the FormRequest now, so it’s all ready when we build the controller. First use artisan to create the skeleton.

Creating the FormRequest with Artisan

~/Code/l5beauty$ php artisan make:request ContactMeRequest
Request created successfully.

Update its contents to match what’s below.

Content of ContactMeRequest.php

<?php
namespace App\Http\Requests;

class ContactMeRequest extends Request
{
  /**
   * Determine if the user is authorized to make this request.
   */
  public function authorize()
  {
    return true;
  }

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

No need to comment on the request. You should be thoroughly familiar with FormRequests after building the administration side of this blog.

Adding the Controller

Now we’ll create the controller we specified in the routes file.

Optionally, you can create the skeleton of the controller with artisan.

Creating ContactController with Artisan

~/Code/l5beauty$ php artisan make:controller --plain ContactController
Controller created successfully.

Make the content of ContactController.php match what’s below.

Content of ContactController.php

<?php
namespace App\Http\Controllers;

use App\Http\Requests\ContactMeRequest;
use Illuminate\Support\Facades\Mail;

class ContactController extends Controller
{
  /**
   * Show the form
   *
   * @return View
   */
  public function showForm()
  {
    return view('blog.contact');
  }

  /**
   * Email the contact request
   *
   * @param ContactMeRequest $request
   * @return Redirect
   */
  public function sendContactInfo(ContactMeRequest $request)
  {
    $data = $request->only('name', 'email', 'phone');
    $data['messageLines'] = explode("\n", $request->get('message'));

    Mail::send('emails.contact', $data, function ($message) use ($data) {
      $message->subject('Blog Contact Form: '.$data['name'])
              ->to(config('blog.contact_email'))
              ->replyTo($data['email']);
    });

    return back()
        ->withSuccess("Thank you for your message. It has been sent.");
  }
}

In the sendContactInfo() method, we use the ContactMeRequest to validate. Then we fill $data with the form fields. For the message field, we break the message into individual lines to pass to the view as messageLines.

Then we use the Mail facade to send the message. You could optionally have the sendContactInfo() method take an Illuminate\Mail\Mailer object as an argument (Laravel 5.1 is smart enough to automatically inject it) and use this object to send mail.

See the Official Documentation for a list of facades and the equivalent class to use if you want to access the instance directly.

After the message is sent, we redirect back to the contact page, passing a success message.

ADD contact_email to your blog config file. It is needed in the controller just created (config('blog.contact_email')). It’s easy to add this configuration value, just edit config/blog.php and add an additional option.

Creating the Views

Two views need to be created for the contact form. The one which will display the form and the one that formats the email to be sent.

Create contact.blade.php in the resources/views/blog directory.

Content of blog.contact view

@extends('blog.layouts.master', ['meta_description' => 'Contact Form'])

@section('page-header')
  <header class="intro-header"
          style="background-image: url('{{ page_image('contact-bg.jpg') }}')">
    <div class="container">
      <div class="row">
        <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
          <div class="site-heading">
            <h1>Contact Me</h1>
            <hr class="small">
            <h2 class="subheading">
              Have questions? I have answers (maybe).
            </h2>
          </div>
        </div>
      </div>
    </div>
  </header>
@stop

@section('content')
  <div class="container">
    <div class="row">
      <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
        @include('admin.partials.errors')
        @include('admin.partials.success')
        <p>
          Want to get in touch with me? Fill out the form below to send me a
          message and I will try to get back to you within 24 hours!
        </p>
        <form action="/contact" method="post">
          <input type="hidden" name="_token" value="{!! csrf_token() !!}">
          <div class="row control-group">
            <div class="form-group col-xs-12">
              <label for="name">Name</label>
              <input type="text" class="form-control" id="name" name="name"
                     value="{{ old('name') }}">
            </div>
          </div>
          <div class="row control-group">
            <div class="form-group col-xs-12">
              <label for="email">Email Address</label>
              <input type="email" class="form-control" id="email" name="email"
                     value="{{ old('email') }}">
            </div>
          </div>
          <div class="row control-group">
            <div class="form-group col-xs-12 controls">
              <label for="phone">Phone Number</label>
              <input type="tel" class="form-control" id="phone" name="phone"
                     value="{{ old('phone') }}">
            </div>
          </div>
          <div class="row control-group">
            <div class="form-group col-xs-12 controls">
              <label for="message">Message</label>
              <textarea rows="5" class="form-control" id="message"
                        name="message">{{ old('message') }}</textarea>
            </div>
          </div>
          <br>
          <div class="row">
            <div class="form-group col-xs-12">
              <button type="submit" class="btn btn-default">Send</button>
            </div>
          </div>
        </form>
      </div>
    </div>
  </div>
@endsection

The blog.contact view should be easy to follow. Notice how we included the errors and success partials from the administration area? They perfectly fit what was needed.

Now create contact.blade.php in the resources/views/emails directory. This is what will format the email we’ll send to ourselves.

Content of email.contact view

<p>
  You have received a new message from your website contact form.
</p>
<p>
  Here are the details:
</p>
<ul>
  <li>Name: <strong>{{ $name }}</strong></li>
  <li>Email: <strong>{{ $email }}</strong></li>
  <li>Phone: <strong>{{ $phone }}</strong></li>
</ul>
<hr>
<p>
  @foreach ($messageLines as $messageLine)
    {{ $messageLine }}<br>
  @endforeach
</p>
<hr>

Nothing too fancy there. Just a smidge of formatting.

Sending the Mail

The contact form should now be fully functional. Fire up your web browser, point it to your http://l5beauty.app project and test it out.

You may notice there’s a slight delay between clicking [Send] and receiving a response of success back. This delay can be especially long when you’re using the smtp mail driver.

Does this delay really need to be there? The answer is no, not if we set up a queue to handle running tasks in the back ground.

About Queues

Queues allow you to defer the processing of time consuming tasks, such as emails. This allows your web requests to respond quicker to the user.

How they Work

Queues are actually quite simple to understand.

Figure 14.1 - Flow Through Queue

Queue Flow

A web request hits the controller where it’s processed. During the processing something is added to the queue (an Email in this figure) and then the response is returned.

Somewhere in the background a queue worker runs. It fetches the next thing from the queue and processes it.

Bam!

The Different Queue Drivers

Laravel provides drivers for several different queue implementation. They are:

  • sync - The sync driver effectively short circuits the entire queuing process. When something is queued and the sync driver is being used, the item is fully processed immediately and synchronously.
  • database - The database driver stores queued items in the local database. Specifically, in a jobs table.
  • beanstalkd - The beanstalkd driver expects beanstalkd to be configured and running. You’ll also have to do a composer require "pda/pheanstalk=~3.0" to use it.
  • sqs - The sqs driver will queue to your Amazon SQS queue. Also composer require aws/aws-sdk-php is required to use it.
  • iron - The iron driver will queue to your IronMQ account and composer required "iron-io/iron_mq=~1.5" is required.
  • redis - The redis driver will store queued items in the Redis database. It requires composer require "predis/predis=~1.0" to operate.

Using the Database Driver

We’ll be using the Database Driver for our queue. To do this we’ll need to create the jobs table and run migrations as instructed below.

Creating and Running Queue Jobs Migration

vagrant@homestead:~/Code/l5beauty$ php artisan queue:table
Migration created successfully!
vagrant@homestead:~/Code/l5beauty$ php artisan migrate
Migrated: 2015_06_06_130436_create_jobs_table

Finally, edit .env and change the QUEUE_DRIVER setting from sync to database.

Queuing the Contact Us Email

Now that the queue is set up, we’re ready to queue emails from the Contact Us form instead of waiting for the delivery to finish before responding to the user.

Changing the Controller

To queue the email, there’s only a single small change to make. Update your ContractController class as instructed.

Change to ContractController.php

// Find the line below
   Mail::send('emails.contact', $data, function ($message) use ($data) {

// And change it to match what's below
   Mail::queue('emails.contact', $data, function ($message) use ($data) {

That’s it! Instead of Mail::send() we call Mail::queue() and Laravel will automatically queue it for us.

Where’s the Email

Test out the Contact Us form again. After you click [Send] there should be no delay before you see the Success message.

Still, you can wait forever for the email and it will never arrive.

Why?

Because there’s no process running in the background, watching for and handling items arriving in the queue.

Running queue:work

To process the next item on the queue, we can manually run artisan’s queue:work command.

This command will do nothing if the queue is empty. But if there’s an item on the queue it will fetch the item and attempt to execute it.

Running Artisan queue:work

vagrant@homestead:~/Code/l5beauty$ php artisan queue:work
Processed: mailer@handleQueuedMessage

As you can see here it handled the queued email message. Now the email should arrive in your inbox within moments.

Automatically Processing the Queue

Of course, having to manually log into our server and run the artisan queue:work each time we want to process the next item on the queue is ridiculous.

There’s a few options to automate this.

One is load up artisan queue:listen in the startup scripts of your server. This command automatically calls artisan queue:work when items appear in the queue.

The problem with this technique is something will invariably happen. The queue:listen command will hang. Or it will stop running. A better way to run queue:listen is with supervisord.

Running queue:listen with supervisord

supervisord is a *nix utility to monitor and control processes. We’re not delving into how to install this utility, but if you have it and get it installed, below is a portion of /etc/supervisord.conf that works well.

Portion of supervisord.conf for queue:listen

[program:l5beauty-queue-listen]
command=php /PATH/TO/l5beauty/artisan queue:listen
user=NONROOT-USER
process_name=%(program_name)s_%(process_num)d
directory=/PATH/TO/l5beauty
stdout_logfile=/PATH/TO/l5beauty/storage/logs/supervisord.log
redirect_stderr=true
numprocs=1

You’ll need to replace the /PATH/TO/ to match your local install. Likewise, the user setting will be unique to your installation.

Using a Scheduled Command

Another option for low volume sites is to schedule queue:work to run every minute. Or even every 5 minutes. This is best done using Laravel 5.1’s command scheduler.

Edit app/Console/Kernel.php and make the changes below.

Editing Console Kernel

// Replace the following method
  /**
   * Define the application's command schedule.
   *
   * @param  Schedule  $schedule
   * @return void
   */
  protected function schedule(Schedule $schedule)
  {
    // Run once a minute
    $schedule->command('queue:work')->cron('* * * * * *');
  }

This will run the queue:work command once a minute. You can change this frequency in many ways.

Various Run Frequencies in Console Kernel

// Run every 5 minutes
$schedule->command('queue:work')->everyFiveMinutes();

// Run once a day
$schedule->command('queue:work')->daily();

// Run Mondays at 8:15am
$schedule->command('queue:work')->weeklyOn(1, '8:15');

To see more options view the documentation.

The second step in setting up the scheduled command is to modify your machine’s crontab. Edit crontab and add the following line.

Crontab Line for Artisan Scheduler

* * * * * php /path/to/artisan schedule:run 1>> /dev/null 2>&1

This will call artisan to run anything currently scheduled, sending any output to the null device.

Queing Jobs

Another great use for queues are asynchronous jobs. These are jobs you execute as normal with $this->dispatch(new JobName) from your controller, but they’ll simply be placed in the queue to be run later by queue:work or whatever method is processing queue items in your application.

Here’s how to do it.

First, when creating the job class, use the --queued option.

Example of a Queued Job

~/Projects/newbeauty$ php artisan make:job --queued TestJob
Job created successfully.

Now, if you examine the template Laravel 5.1 created for TestJob you’ll notice few small changes at the top.

Difference in Queued Jobs

// These three use statements are new
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

// The class will also implement ShouldQueue
class TestJob extends Job implements SelfHandling, ShouldQueue
{

// And the class uses two traits
    use InteractsWithQueue, SerializesModels;
ShouldQueue
By having the TestJobclass implement ShouldQueue, the handle() method won’t be called. Instead, TestJob will be constructed and the instance will be pushed onto the queue. When the item is processed from the queue, then the handle() method will be called..
InteractsWithQueue
This will make several queue interaction methods available such as $this->delete() to delete the item from the queue or $this->release() to release the item back onto the queue. Normally you won’t need these methods.
SerializesModels
When the job is serialized to be placed on the queue, this Trait will look for properties of the Job that are models and serialize them correctly.

Queued Jobs are an excellent way to run time consuming processes which you don’t want the user to have to wait for.

Recap

The main thing accomplished in this chapter was adding a Contact form to the blog, but we covered several interesting topics to do it. We talked about sending mail with Mailgun and testing mail with Tinker. Several alternative routing methods were presented. And we discussed queues, set up a database queue, and sent any contact emails through the queue.

A pretty solid chapter.

comments powered by Disqus