Custom Mail Driver for Laravel

Out of the box Laravel supports many mail drivers, such as smtp, sendmail, mailgun, log, array, etc. But what if you wold like to use a mail service that Laravel does not have a driver for, such as Mailjet?  You can create a custom driver for that service.  This article will describe how to create a driver for Mailjet service.

The mailjet driver will be created as a Laravel package, so let’s get the app’s composer.json file ready. We will add our package to the require block of composer.json file:

"require": {
        "php": ">=7.0.0",
        "fideloper/proxy": "~3.3",
        "laravel/framework": "5.5.*",
        "laravel/tinker": "~1.0",
        "alexrusin/my-mailjet": "master@dev"
    }

Down on the bottom of composer.json  file we will add repositories block that will point to our package. The package will be located right outside Laravel app’s directory.

"repositories": [ 
      {
        "type": "path",
        "url": "./../my-mailjet"
      }
    ]

Let’s create the package and place it in the same directory where Laravel app is located. We will start with the following directory structure:

Place the following code in package’s composer.json file:

{
"name": "alexrusin/my-mailjet",
"description": "MailJet transport for Laravel",
"license": "MIT",
"authors": [
{
"name": "Alex Rusin",
"homepage": "https://github.com/alexrusin",
"role": "Developer"
}
],
"require": {
"php": ">=5.6.4",
"mailjet/mailjet-apiv3-php": "^1.4"
},

"autoload": {
"psr-4": {
"Apr\\MailJet\\": "src/"
}
},

"extra": {
"laravel": {
"providers": [
"Apr\\MailJet\\MailJetServiceProvider"
]
}
},
"minimum-stability": "dev"
}

The code above has basic description of the package, it lists a dependency mailjet/mailjet-apiv3-php that will be used to make calls to the service, it sets up psr-4 autoloading, and it enables auto discovery for Laravel 5.5 or greater.  After composer.json is ready, we can start working on the package’s source files. Below is a full structure of the package:

Let’s start with MailJetServiceProvider.php


<?php 
namespace Apr\MailJet; 

use Apr\MailJet; 
use Apr\MailJet\Transport\MailJetAddedTransportManager; 
use Illuminate\Mail\MailServiceProvider; 
use Illuminate\Support\ServiceProvider; 

class MailJetServiceProvider extends MailServiceProvider 
{ 
    public function boot() 
    { 
    $this->publishes([
            __DIR__.'/config/mailjet.php' => config_path('mailjet.php')
        ], 'mjconfig');
    }

    protected function registerSwiftTransport()
    {
        $this->app->singleton('swift.transport', function ($app) {
            return new MailJetAddedTransportManager($app);
        });
    }
}

MailJetServiceProvider extends Laravel’s MailServiceProvider. It’s main goal is to override registerSwiftTransport() function and bind our own class to ‘swift.transport’. MailJetAddedTransportManager class extends Laravel’s TransportManager and creates Mailjet transport.  In order to better understand how Laravel’s mail drivers work tate a look at vendor/laravel/framework/src/Illuminate/Mail. The service provider also publishes mailjet.php configuration file.  Configuration file looks like this:


<?php 
return [ 
    'public_key' => env('MAILJET_PUBLIC_KEY'),
    'secret_key' => env('MAILJET_SECRET_KEY')
];

After the package is installed, you have to put your Mailjet’s private and public keys in .env file.

Up next is MailJetAddedTransportManager.php 

<?php

namespace Apr\MailJet\Transport;

use Illuminate\Mail\TransportManager;

class MailJetAddedTransportManager extends TransportManager
{
protected function createMailjetDriver()
{
return new MailJetTransport;
}
}

This class extends Laravel’s TransportManager class and creates MailJetTransport.  This transport looks the following:

<?php

namespace Apr\MailJet\Transport;

use Illuminate\Mail\Transport\Transport;
use Swift_Mime_SimpleMessage;

class MailJetTransport extends Transport
{

protected $publicKey;
protected $secretKey;

public function __construct()
{
$this->publicKey = config('mailjet.public_key');
$this->secretKey = config('mailjet.secret_key');
}

public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null)
{

$this->beforeSendPerformed($message);

$mj = new \Mailjet\Client($this->publicKey, $this->secretKey,
true,['version' => 'v3.1']);

$response = $mj->post(\Mailjet\Resources::$Email, ['body' => $this->getBody($message)]);

$this->sendPerformed($message);

return $this->numberOfRecipients($message);
}

/**
* Get body for the message.
*
* @param \Swift_Mime_SimpleMessage $message
* @return array
*/

protected function getBody(Swift_Mime_SimpleMessage $message)
{
return [
'Messages' => [
[
'From' => [
'Email' => config('mail.from.address'),
'Name' => config('mail.from.name')
],
'To' => $this->getTo($message),
'Subject' => $message->getSubject(),
'HTMLPart' => $message->getBody(),
]
]
];
}

/**
* Get the "to" payload field for the API request.
*
* @param \Swift_Mime_SimpleMessage $message
* @return string
*/
protected function getTo(Swift_Mime_SimpleMessage $message)
{
return collect($this->allContacts($message))->map(function ($display, $address) {
return $display ? [
'Email' => $address,
'Name' =>$display
] : [
'Email' => $address,
];

})->values()->toArray();
}

/**
* Get all of the contacts for the message.
*
* @param \Swift_Mime_SimpleMessage $message
* @return array
*/
protected function allContacts(Swift_Mime_SimpleMessage $message)
{
return array_merge(
(array) $message->getTo(), (array) $message->getCc(), (array) $message->getBcc()
);
}
}

MailJetTransport implements send() function using MailJet’s package. It puts the body of the email and contacts in the format it needs.
At this point package is ready to go.  It is not a comprehensive version but it is a good start. The source code if available here: https://github.com/alexrusin/my-mailjet

To install the package we run the following commands:


composer update

php artisan vendor:publish --tag=mjconfig

This will pull the package in Laravel app, auto discover it and publish the config file.  Now in your .env set the following:


MAIL_DRIVER=mailjet

MAILJET_PUBLIC_KEY=your_mailjet_public_key
MAILJET_SECRET_KEY=your_mailjet_secret_key

and you will be able to send emails via Mailjet.

Resources used:
Mailjet Email API Documentation

Mail Logging in Laravel 5.3: Extending the Mail Driver