s-ichikawa/laravel-sendgrid-driver

Laravel 7.28: Unsupported mail transport [sendgrid].

alwintom opened this issue ยท 4 comments

I have been using your library (version 2) with laravel 6.x without any issues.

Recently I started a new project which is based on Laravel 7.x

I followed the instructions and installed version "~3.0", and the configured everything correctly.

But when I try to send an email, without specifying any mailer explicitly, I get the following error:
Unsupported mail transport [sendgrid].

However, if I change the .env variable MAIL_MAILER to log and then specify the mailer sendgrid explicitly while sending the email, everything seems to work.

I did some debugging and found that in the file SendgridTransportServiceProvider.php, the section where the sendgrid transport is registered is not executed when default mailer is set to sendgrid. The application throws the exception before reaching here.

$this->app->afterResolving(MailManager::class, function (MailManager $mail_manager) {
            < DOES NOT REACH HERE >
            $mail_manager->extend("sendgrid", function ($config) {
                if (! isset($config['api_key'])) {
                    $config = $this->app['config']->get('services.sendgrid', []);
                }
                $client = new HttpClient(Arr::get($config, 'guzzle', []));
                $endpoint = isset($config['endpoint']) ? $config['endpoint'] : null;

                return new SendgridTransport($client, $config['api_key'], $endpoint);
            });

});

I don't know why it happens. I tried to reproduce by the steps below but It seems work...

  • Create a project and install this library
$ composer create-project laravel/laravel:^v7.0 laravel728
Creating a "laravel/laravel:^v7.0" project at "./laravel728"
Installing laravel/laravel (v7.28.0)
  - Installing laravel/laravel (v7.28.0): Loading from cache
...
$ cd laravel728
$ composer require s-ichikawa/laravel-sendgrid-driver
Using version ^3.0 for s-ichikawa/laravel-sendgrid-driver
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing s-ichikawa/laravel-sendgrid-driver (3.0.2): Loading from cache
...
  • edit .env
MAIL_MAILER=sendgrid
SENDGRID_API_KEY="MY.API.KEY"
  • add sendgrid config to config/services.php
    'sendgrid' => [
        'api_key' => env('SENDGRID_API_KEY'),
    ],
  • add sendgrid config to config/mail.php
    'mailers' => [
       ...
        'sendgrid' => [
            'transport' => 'sendgrid',
        ],
  • Create a sample command similar to this.

Are there any settings missing in your env?

@s-ichikawa

Actually I tried another custom mail driver as well (Mailjet) and that also behaves the same way.

The project that I am working on was worked on by some other developers previously and looks like they had originally started the project using Laravel 6, and then upgraded to Laravel 7.

So went through Laravel's vendor files by putting breakpoints in it, and it looks like an issue with either Laravel or the Project itself. I found that laravel does not resolve MailManager::class until the first call is made to Mail::. When it does try to resolve it, it simply creates and instance of MailManager class and returns it. It then immediately tries to create a transport defined by the MAIL_MAILER, and if that is set to a sendgrid based one, it throws the exception. For some reason afterResolving() method is not called when I expect it to . This is where the custom sendgrid driver is being registered.

When the MAIL_MAILER is set to an SMTP or LOG driver, the MailManager class resolves and afterResolving() is called and sendgrid transport gets registered. This is why Mail::mailer('sendgrid')->send() seems to work.

I tried deleting all vendor files and installing the packges again, but that has not solved the issue. Finally I had to write a custom MailServiceProvider replacing the one provided by Laravel and registered the sendgrid transport here and this seems to have fixed the issue. So my custom MailSerivceProvider looks like this now:

/**
     * Register the Illuminate mailer instance.
     * Also register sendgrid and mailjet transports.
     * 
     * @return void
     */
    protected function registerIlluminateMailer()
    {
        $this->app->singleton('mail.manager', function ($app) {
            $manager = new MailManager($app);
            $this->registerCustomTransports($manager);    // Registers sengrid and other custom drivers.
            return $manager;
        });

        $this->app->bind('mailer', function ($app) {
            return $app->make('mail.manager')->mailer();
        });
    }

So I guess the issue is definitely not with your package, and so you can close this issue.

I sent emails via SendGrid successfully in my local environment, but when I deploy to Google App Engine and try to send via SendGrid with the same code, I get the same problem.

I want to work around the problem in the same way as @alwintom, but I can't find the function $this->registerCustomTransports($manager);.

Please kindly advise ๐Ÿ™‡โ€โ™‚๏ธ๐Ÿ™

Laravel version is 7.26.0.

@ucan-lab
Sorry about that, but that is only part of the file. I will post the whole file here:

CustomEmailServiceProvider.php

namespace App\Providers;

use GuzzleHttp\Client as HttpClient;
use Illuminate\Mail\MailManager;
use Illuminate\Mail\MailServiceProvider;
use Illuminate\Support\Arr;
use Sichikawa\LaravelSendgridDriver\Transport\SendgridTransport;

class CustomMailServiceProvider extends MailServiceProvider
{

    /**
     * Register the Illuminate mailer instance.
     * Also register sendgrid and mailjet transports.
     * 
     * @return void
     */
    protected function registerIlluminateMailer()
    {
        $this->app->singleton('mail.manager', function ($app) {
            $manager = new MailManager($app);
            $this->registerCustomTransports($manager);
            return $manager;
        });

        $this->app->bind('mailer', function ($app) {
            return $app->make('mail.manager')->mailer();
        });
    }

    protected function registerCustomTransports(MailManager $manager) {
        $this->registerSendgridTransport($manager);
    }

    protected function registerSendgridTransport(MailManager $manager) {
        $manager->extend("sendgrid", function ($config) {
            if (! isset($config['api_key'])) {
                $config = $this->app['config']->get('services.sendgrid', []);
            }
            $client = new HttpClient(Arr::get($config, 'guzzle', []));
            $endpoint = isset($config['endpoint']) ? $config['endpoint'] : null;

            return new SendgridTransport($client, $config['api_key'], $endpoint);
        });
    }
}

Afterwards, you will need to remove
Illuminate\Mail\MailServiceProvider::class,
and add
App\Providers\CustomEmailServiceProvider::class
in your config/app.php provider's list

Also if you have package discovery enabled, add an exclusion in composer.json so that there is no duplication

"extra": {
        "laravel": {
            "dont-discover": [
                "s-ichikawa/laravel-sendgrid-driver",
            ]
        }
    },