Skip to content

Instantly share code, notes, and snippets.

@arthursalvtr
Last active April 5, 2024 17:43
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save arthursalvtr/4c67f82eb3cb083449d3be3aa88e1ae1 to your computer and use it in GitHub Desktop.
Save arthursalvtr/4c67f82eb3cb083449d3be3aa88e1ae1 to your computer and use it in GitHub Desktop.
Add Custom Mail Driver to Laravel

How to add custom Mail Driver to Laravel

Tested on Laravel 5.8

Within the source code of laravel 5.8 at this time of writing, we have this following

<?php

namespace Illuminate\Support;
// use...

abstract class Manager
{
  //somewhere in the body ...
  
      /**
     * Register a custom driver creator Closure.
     *
     * @param  string    $driver
     * @param  \Closure  $callback
     * @return $this
     */
    public function extend($driver, Closure $callback)
    {
        $this->customCreators[$driver] = $callback;

        return $this;
    }
    
    // the rest of the code...
}

it means we can easily extend the driver using this manager class!

Create a MailDriver class

In this case I am using OutlookMailDriver, and since i am going to use the extend method from the existing manager, it expects a Closure therefore i make an invocable method that return a function.

<?php
declare(strict_types=1);

namespace App\Helpers\Drivers;


use App\Helpers\Transports\OutlookTransportManager;
use Illuminate\Mail\TransportManager;

class OutlookMailDriver
{
    public function __invoke(TransportManager $manager)
    {
        return function ($app) {
            $config = $app['config']->get('services.outlook', []);
            return new OutlookTransportManager($config);
        };
    }
}

The signature of your invocable may differ from mine

Create a CustomMailProvider that extend Illuminate\Mail\MailServiceProvider

I am going to override the registerSwiftTransport method yet calling its parent first to have it registered its expected Manager then I extend it with my custom Mail Driver.

<?php

namespace App\Providers;

use App\Helpers\Drivers\OutlookMailDriver;
use Illuminate\Mail\MailServiceProvider;
use Illuminate\Mail\TransportManager;

class CustomMailProvider extends MailServiceProvider
{
    /**
     * Register the Swift Transport instance.
     *
     * @return void
     */
    protected function registerSwiftTransport()
    {
        parent::registerSwiftTransport();
        
        $this->app->extend('swift.transport', function (TransportManager $transport) {
            $driver = 'outlook';
            $callback = new OutlookMailDriver();
            $transport->extend($driver, $callback($transport));
            return $transport;
        });
    }
}

Create a class that extends Illuminate\Mail\Transport\Transport

Since Illuminate\Mail\Transport\Transport is an abstract class therefore i need to have send method.

<?php
declare(strict_types=1);

namespace App\Helpers\Transports;

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

class OutlookTransportManager extends Transport
{

    public function __construct($config)
    {
        // do your whatever you need here
    }
    
    public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null)
    {
        logger('I am using custom outlook manager');
        // TODO: Implement send() method.
        return 0;
    }
}

Register the CustomMailProvider in your config/app.php

comment Illuminate\Mail\MailServiceProvider::class within your config, and register your own custom provider there

return [
    'providers' => [
        //...
        App\Providers\CustomMailProvider::class,
        //Illuminate\Mail\MailServiceProvider::class, // comment this out
    ],
];

Test

// somewhere in your route/web.php

Route::get('test', function () {
    Mail::to('test@example.com')->send(new \App\Mail\Test());
});
@VeryStrongFingers
Copy link

How come you're extending (decorating) TransportManager instead of just adding a custom driver?

$this->app['swift.transport']->extends('outlook', static function ($app) {
      return $app->make(OutlookMailDriver::class);
});

@MgHtinLynn
Copy link

Can you help Laravel 7, 8 also, please?

@arthursalvtr
Copy link
Author

arthursalvtr commented Dec 16, 2020

Can you help Laravel 7, 8 also, please?

@MgHtinLynn

for laravel 8 you can try

        $this->app['mail.manager']->extend('customDriver', function (array $config) {
            /**
             * you have to return \Illuminate\Mail\Transport\Transport
             */
            return new YourCustomTransport();
        });

remember to add your custom driver and transport at config/mail.php

    'mailers' => [
        'customDriver' => [
            'transport' =>  'customDriver',
        ],
    ],

@alkhachatryan
Copy link

For Laravel 8 you can use already ready vendor/laravel/framework/src/Illuminate/Mail/Transport/MailgunTransport.php

@ramaID
Copy link

ramaID commented Feb 3, 2023

Can you help Laravel 7, 8 also, please?

@MgHtinLynn

for laravel 8 you can try

        $this->app['mail.manager']->extend('customDriver', function (array $config) {
            /**
             * you have to return \Illuminate\Mail\Transport\Transport
             */
            return new YourCustomTransport();
        });

remember to add your custom driver and transport at config/mail.php

    'mailers' => [
        'customDriver' => [
            'transport' =>  'customDriver',
        ],
    ],

I think this save my day, thanks for sharing 🫡

@MgHtinLynn
Copy link

MgHtinLynn commented Feb 3, 2023

Thank you. It's a lot of help for me.

@yushine
Copy link

yushine commented Nov 15, 2023

Cannot find the class Illuminate\Mail\Transport\Transport in Laravel 9, how to resolve it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment