10

I want to override the Laravels' Mail's classes facade method send (just intercept it forcing some checks and then if it passes triggering parent::send())

What is the best way to do this?

naneri
  • 3,771
  • 2
  • 29
  • 53
  • 1
    You shouldn't have the Mailer perform checks, that's not its job, it just sends mail. The logic should be performed outside, and if the mail should send, call the send method. – tam5 Sep 22 '16 at 21:37
  • 1
    @tam You are talking about maintainability and structure, and I do know, that it is better not to give the responsibility to mailer. But the question is not about that. – naneri Sep 23 '16 at 08:17

1 Answers1

14

A Facade doesn't work like that. It's essentially kind of like a wrapper class that calls the underlying class that it represents.

The Mail facade doesn't actually have a send method. When you do Mail::send(), under the hood, the "facade accessor" is used to reference an instance of the Illuminate\Mail\Mailer class bound in the IoC container. It's on that object the send method is called.

The way in which you can achieve what you're after is actually a little bit trickier than it seems. What you can do is:

  • Write your own implementation of Mailer, extending Illuminate\Mail\Mailer, in which you can override the send method, implement your checks and call parent::send().
  • Write your own service provider (Extending Illuminate\Mail\MailServiceProvider), in particular re-implement the register method. It should create an instance of your own Mailer in place of Laravel's own. (You can copy most of the code from Laravel's register method).
  • Now, in your config/app.php file, in the providers array, replace Illuminate\Mail\MailServiceProvider::class, with your own provider.

That should let you hook into Laravel's Mail functionality.


For more information, you can take a look at the following question/answer which achieves a similar thing. It extends the Mail functionality to add a new transport driver, but it takes a similar approach in that it provides its own Mailer implementation and service provider.

Add a new transport driver to Laravel's Mailer


app/MyMailer/Mailer.php

<?php

namespace App\MyMailer;

class Mailer extends \Illuminate\Mail\Mailer
{
    public function send($view, array $data = [], $callback = null)
    {
        // Do your checks

        return parent::send($view, $data, $callback);
    }
}

app/MyMailer/MailServiceProvider.php (Most of the code copied from Laravel's MailServiceProvider class)

<?php

namespace App\MyMailer;

class MailServiceProvider extends \Illuminate\Mail\MailServiceProvider
{
    public function register()
    {
        $this->registerSwiftMailer();

        $this->app->singleton('mailer', function ($app) {
            // This is YOUR mailer - notice there are no `use`s at the top which
            // Looks for a Mailer class in this namespace
            $mailer = new Mailer(
                $app['view'], $app['swift.mailer'], $app['events']
            );

            $this->setMailerDependencies($mailer, $app);


            $from = $app['config']['mail.from'];

            if (is_array($from) && isset($from['address'])) {
                $mailer->alwaysFrom($from['address'], $from['name']);
            }

            $to = $app['config']['mail.to'];

            if (is_array($to) && isset($to['address'])) {
                $mailer->alwaysTo($to['address'], $to['name']);
            }

            return $mailer;
        });
    }
}

config/app.php (In the providers array)

//...
// Illuminate\Mail\MailServiceProvider::class,
App\MyMailer\MailServiceProvider::class,
//...
danronmoon
  • 3,814
  • 5
  • 34
  • 56
Jonathon
  • 15,873
  • 11
  • 73
  • 92
  • How can I retrieve the email from the callback? – naneri Sep 22 '16 at 20:55
  • What do you mean? – Jonathon Sep 22 '16 at 23:15
  • @Jonathon, I know that you have written this answer a long time ago. I have overridden mail::send using the suggested method and have successfully been able to manipulate it.I am currently encountering a problem that the $message variable is undefined (I am using the $message->embed function) in my email templates. According to laravel documentation the $message variable should be passed automatically. How I can make sure that continues to be passed without having to go through all the email templates? – perpetual_dream Dec 22 '21 at 21:28