Skip to content

Commit

Permalink
Merge pull request #39 from joelbutcher/fix-mail-facade-instance-swap
Browse files Browse the repository at this point in the history
Fix the `Mail::swap()` facade override used in Laravel 10
  • Loading branch information
mpociot authored Mar 14, 2023
2 parents f0ee2ac + a064e60 commit 0ebf33e
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 108 deletions.
120 changes: 120 additions & 0 deletions src/CreatesMailers.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php

namespace BeyondCode\HeloLaravel;

use Illuminate\Support\Arr;
use Illuminate\Support\Str;

trait CreatesMailers
{
protected function createLaravel6Mailer($app)
{
$config = $this->getConfig();

// Once we have create the mailer instance, we will set a container instance
// on the mailer. This allows us to resolve mailer classes via containers
// for maximum testability on said classes instead of passing Closures.
$mailer = new Mailer(
$app['view'], $app['swift.mailer'], $app['events']
);

if ($app->bound('queue')) {
$mailer->setQueue($app['queue']);
}

// Next we will set all of the global addresses on this mailer, which allows
// for easy unification of all "from" addresses as well as easy debugging
// of sent messages since they get be sent into a single email address.
foreach (['from', 'reply_to', 'to'] as $type) {
$this->setGlobalAddress($mailer, $config, $type);
}

return $mailer;
}

protected function createLaravel7Mailer($app)
{
$defaultDriver = $app['mail.manager']->getDefaultDriver();
$config = $this->getConfig($defaultDriver);

// Laravel 7 no longer bindes the swift.mailer:
$swiftMailer = new Swift_Mailer($app['mail.manager']->createTransport($config));

// Once we have create the mailer instance, we will set a container instance
// on the mailer. This allows us to resolve mailer classes via containers
// for maximum testability on said classes instead of passing Closures.
$mailer = new Laravel7Mailer(
'smtp', $app['view'], $swiftMailer, $app['events']
);

if ($app->bound('queue')) {
$mailer->setQueue($app['queue']);
}

// Next we will set all of the global addresses on this mailer, which allows
// for easy unification of all "from" addresses as well as easy debugging
// of sent messages since they get be sent into a single email address.
foreach (['from', 'reply_to', 'to', 'return_path'] as $type) {
$this->setGlobalAddress($mailer, $config, $type);
}

return $mailer;
}

protected function createLaravel9Mailer($app)
{
$defaultDriver = $app['mail.manager']->getDefaultDriver();
$config = $this->getConfig($defaultDriver);

// We get Symfony Transport from Laravel 9 mailer
$symfonyTransport = $app['mail.manager']->getSymfonyTransport();

// Once we have create the mailer instance, we will set a container instance
// on the mailer. This allows us to resolve mailer classes via containers
// for maximum testability on said classes instead of passing Closures.
$mailer = new Laravel7Mailer(
'smtp', $app['view'], $symfonyTransport, $app['events']
);

if ($app->bound('queue')) {
$mailer->setQueue($app['queue']);
}

// Next we will set all of the global addresses on this mailer, which allows
// for easy unification of all "from" addresses as well as easy debugging
// of sent messages since they get be sent into a single email address.
foreach (['from', 'reply_to', 'to', 'return_path'] as $type) {
$this->setGlobalAddress($mailer, $config, $type);
}

return $mailer;
}

protected function getConfig($name = 'smtp')
{
return $this->app['config']['mail.driver']
? $this->app['config']['mail']
: $this->app['config']["mail.mailers.{$name}"];
}

/**
* Set a global address on the mailer by type.
*
* @param \Illuminate\Mail\Mailer $mailer
* @param array $config
* @param string $type
* @return void
*/
protected function setGlobalAddress($mailer, array $config, $type)
{
if (version_compare(app()->version(), '7.0.0', '<')) {
$address = Arr::get($config, $type);
} else {
$address = Arr::get($config, $type, $this->app['config']['mail.'.$type]);
}

if (is_array($address) && isset($address['address'])) {
$mailer->{'always' . Str::studly($type)}($address['address'], $address['name']);
}
}
}
122 changes: 15 additions & 107 deletions src/HeloLaravelServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
namespace BeyondCode\HeloLaravel;

use Illuminate\Contracts\Mail\Mailer as MailerContract;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use Swift_Mailer;

class HeloLaravelServiceProvider extends ServiceProvider
{
use CreatesMailers;

/**
* Bootstrap the application services.
*/
Expand All @@ -21,11 +21,8 @@ public function boot()
return;
}

$instance = app()->make(Mailer::class);

Mail::swap($instance);

app()->instance(MailerContract::class, $instance);
$this->bootMailable();

if ($this->app->runningInConsole()) {
View::addNamespace('helo', __DIR__ . '/../resources/views');
Expand All @@ -50,7 +47,7 @@ public function register()
$this->mergeConfigFrom(__DIR__.'/../config/helo.php', 'helo');

$this->app->singleton(Mailer::class, function ($app) {
$version = (int) Str::of($app->version())->explode('.')->first();
$version = $this->version($app);

if ($version < 7) {
return $this->createLaravel6Mailer($app);
Expand All @@ -63,114 +60,25 @@ public function register()
});
}

protected function createLaravel6Mailer($app)
protected function bootMailable()
{
$config = $this->getConfig();
if ($this->version() < 10) {
$instance = app()->make(Mailer::class);

// Once we have create the mailer instance, we will set a container instance
// on the mailer. This allows us to resolve mailer classes via containers
// for maximum testability on said classes instead of passing Closures.
$mailer = new Mailer(
$app['view'], $app['swift.mailer'], $app['events']
);
Mail::swap($instance);

if ($app->bound('queue')) {
$mailer->setQueue($app['queue']);
}

// Next we will set all of the global addresses on this mailer, which allows
// for easy unification of all "from" addresses as well as easy debugging
// of sent messages since they get be sent into a single email address.
foreach (['from', 'reply_to', 'to'] as $type) {
$this->setGlobalAddress($mailer, $config, $type);
}

return $mailer;
}

protected function createLaravel7Mailer($app)
{
$defaultDriver = $app['mail.manager']->getDefaultDriver();
$config = $this->getConfig($defaultDriver);

// Laravel 7 no longer bindes the swift.mailer:
$swiftMailer = new Swift_Mailer($app['mail.manager']->createTransport($config));

// Once we have create the mailer instance, we will set a container instance
// on the mailer. This allows us to resolve mailer classes via containers
// for maximum testability on said classes instead of passing Closures.
$mailer = new Laravel7Mailer(
'smtp', $app['view'], $swiftMailer, $app['events']
);

if ($app->bound('queue')) {
$mailer->setQueue($app['queue']);
}

// Next we will set all of the global addresses on this mailer, which allows
// for easy unification of all "from" addresses as well as easy debugging
// of sent messages since they get be sent into a single email address.
foreach (['from', 'reply_to', 'to', 'return_path'] as $type) {
$this->setGlobalAddress($mailer, $config, $type);
}

return $mailer;
}

protected function createLaravel9Mailer($app)
{
$defaultDriver = $app['mail.manager']->getDefaultDriver();
$config = $this->getConfig($defaultDriver);

// We get Symfony Transport from Laravel 9 mailer
$symfonyTransport = $app['mail.manager']->getSymfonyTransport();

// Once we have create the mailer instance, we will set a container instance
// on the mailer. This allows us to resolve mailer classes via containers
// for maximum testability on said classes instead of passing Closures.
$mailer = new Laravel7Mailer(
'smtp', $app['view'], $symfonyTransport, $app['events']
);

if ($app->bound('queue')) {
$mailer->setQueue($app['queue']);
}

// Next we will set all of the global addresses on this mailer, which allows
// for easy unification of all "from" addresses as well as easy debugging
// of sent messages since they get be sent into a single email address.
foreach (['from', 'reply_to', 'to', 'return_path'] as $type) {
$this->setGlobalAddress($mailer, $config, $type);
$this->app->instance(MailerContract::class, $instance);
} else {
Mail::swap(new MailManager($this->app));
}

return $mailer;
}

protected function getConfig($name = 'smtp')
{
return $this->app['config']['mail.driver']
? $this->app['config']['mail']
: $this->app['config']["mail.mailers.{$name}"];
}

/**
* Set a global address on the mailer by type.
*
* @param \Illuminate\Mail\Mailer $mailer
* @param array $config
* @param string $type
* @return void
*/
protected function setGlobalAddress($mailer, array $config, $type)
private function version($app = null): int
{
if (version_compare(app()->version(), '7.0.0', '<')) {
$address = Arr::get($config, $type);
} else {
$address = Arr::get($config, $type, $this->app['config']['mail.'.$type]);
if (! $app) {
$app = $this->app;
}

if (is_array($address) && isset($address['address'])) {
$mailer->{'always' . Str::studly($type)}($address['address'], $address['name']);
}
return (int) collect(explode('.', $app->version()))->first();
}
}
20 changes: 20 additions & 0 deletions src/MailManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace BeyondCode\HeloLaravel;

use Illuminate\Contracts\Mail\Factory as FactoryContract;
use Illuminate\Mail\MailManager as LaravelMailManager;

class MailManager extends LaravelMailManager implements FactoryContract
{
use CreatesMailers;

public function mailer($name = null)
{
if (! $name) {
return $this->createLaravel9Mailer($this->app);
}

return $this->mailers[$name] = $this->get($name);
}
}
2 changes: 1 addition & 1 deletion src/Mailer.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function send($view, array $data = [], $callback = null)
protected function applyDebugHeaders(Mailable $mailable)
{
$methodName = method_exists($mailable, 'withSymfonyMessage') ? 'withSymfonyMessage' : 'withSwiftMessage';

$mailable->$methodName(function ($message) use ($mailable) {
$viewFile = $view = $viewContent = $viewData = null;

Expand Down

0 comments on commit 0ebf33e

Please sign in to comment.