Давайте поговорим о процессе отправки почты Laravel

задняя часть Laravel Amazon Markdown

Когда я использую функцию отправки электронной почты Laravel, у меня возникает несколько вопросов:

  1. Laravel интегрирует SMTP, Mailgun, SparkPost, Amazon SES и другие драйверы, как он это делает?

  2. Laravel поддерживает полнотекстовый формат, формат веб-страницы и формат Markdown, как это реализовано?

  3. Как выглядит весь процесс отправки электронной почты?

Начнем разбираться с принципом реализации «функции отправки почты» голыми руками.

написать демо

Мы используем бесплатную почту, предоставляемую Alibaba Cloud, и используем драйвер «smtp» в качестве теста, для справки..envКонфигурация:

MAIL_DRIVER=smtp
MAIL_HOST=smtp.mxhichina.com
MAIL_PORT=25
MAIL_USERNAME=***@coding01.cn
MAIL_PASSWORD=****
MAIL_ENCRYPTION=tls
MAIL_FROM=***@coding01.cn
MAIL_NAME=coding01

Написать тестовый процесс довольно просто:

// 1. 创建测试类
php artisan make:mail TestEmail

// 2. 在 TestEmail 类,载入视图

public function build()
{
    return $this->view('mail.test');
}

// 3. 输出 hello coding01

<p>hello coding01</p>

Наконец, напишите командную функцию:

Artisan::command('test', function () {
    Mail::to('yemeishu@126.com')->send(new \App\Mail\TestEmail());
});

воплощать в жизньphp artisan testПосмотрите, успешно ли отправлен тест:

Разобрать MailServiceProvider

Написал много кода Laravel, см.

 Mail::to('yemeishu@126.com')->send(new \App\Mail\TestEmail());

Естественно, мне интересно, есть лиMailServiceProvider, как и ожидалось, вconfig/app.phpмассивprovidersвключаетServiceProvider

Итак, мы начали примерно с этогоMailServiceProviderанализировать

/**
 * Register the service provider.
 *
 * @return void
 */
public function register()
{
    $this->registerSwiftMailer();

    $this->registerIlluminateMailer();

    $this->registerMarkdownRenderer();
}

СмотретьregisterФункция, с первого взгляда, мы сосредоточимся на том, для чего используются эти три метода.

registerSwiftMailer

Посмотрите на код:

/**
 * Register the Swift Mailer instance.
 *
 * @return void
 */
public function registerSwiftMailer()
{
    $this->registerSwiftTransport();

    // Once we have the transporter registered, we will register the actual Swift
    // mailer instance, passing in the transport instances, which allows us to
    // override this transporter instances during app start-up if necessary.
    $this->app->singleton('swift.mailer', function ($app) {
        if ($domain = $app->make('config')->get('mail.domain')) {
            Swift_DependencyContainer::getInstance()
                            ->register('mime.idgenerator.idright')
                            ->asValue($domain);
        }

        return new Swift_Mailer($app['swift.transport']->driver());
    });
}

Это легко понять, это регистрацияSwift Mailerпример. Перед созданием экземпляра выполните$this->registerSwiftTransport();метод:

/**
 * Register the Swift Transport instance.
 *
 * @return void
 */
protected function registerSwiftTransport()
{
    $this->app->singleton('swift.transport', function ($app) {
        return new TransportManager($app);
    });
}

посмотри на этоTransportManagerДля чего нужен класс:

<?php

namespace Illuminate\Mail;

use Aws\Ses\SesClient;
use Illuminate\Support\Arr;
use Psr\Log\LoggerInterface;
use Illuminate\Support\Manager;
use GuzzleHttp\Client as HttpClient;
use Swift_SmtpTransport as SmtpTransport;
use Illuminate\Mail\Transport\LogTransport;
use Illuminate\Mail\Transport\SesTransport;
use Illuminate\Mail\Transport\ArrayTransport;
use Swift_SendmailTransport as MailTransport;
use Illuminate\Mail\Transport\MailgunTransport;
use Illuminate\Mail\Transport\MandrillTransport;
use Illuminate\Mail\Transport\SparkPostTransport;
use Swift_SendmailTransport as SendmailTransport;

class TransportManager extends Manager
{
    /**
     * Create an instance of the SMTP Swift Transport driver.
     *
     * @return \Swift_SmtpTransport
     */
    protected function createSmtpDriver()
    {
        $config = $this->app->make('config')->get('mail');

        // The Swift SMTP transport instance will allow us to use any SMTP backend
        // for delivering mail such as Sendgrid, Amazon SES, or a custom server
        // a developer has available. We will just pass this configured host.
        $transport = new SmtpTransport($config['host'], $config['port']);

        if (isset($config['encryption'])) {
            $transport->setEncryption($config['encryption']);
        }

        // Once we have the transport we will check for the presence of a username
        // and password. If we have it we will set the credentials on the Swift
        // transporter instance so that we'll properly authenticate delivery.
        if (isset($config['username'])) {
            $transport->setUsername($config['username']);

            $transport->setPassword($config['password']);
        }

        // Next we will set any stream context options specified for the transport
        // and then return it. The option is not required any may not be inside
        // the configuration array at all so we'll verify that before adding.
        if (isset($config['stream'])) {
            $transport->setStreamOptions($config['stream']);
        }

        return $transport;
    }

    /**
     * Create an instance of the Sendmail Swift Transport driver.
     *
     * @return \Swift_SendmailTransport
     */
    protected function createSendmailDriver()
    {
        return new SendmailTransport($this->app['config']['mail']['sendmail']);
    }

    /**
     * Create an instance of the Amazon SES Swift Transport driver.
     *
     * @return \Illuminate\Mail\Transport\SesTransport
     */
    protected function createSesDriver()
    {
        $config = array_merge($this->app['config']->get('services.ses', []), [
            'version' => 'latest', 'service' => 'email',
        ]);

        return new SesTransport(new SesClient(
            $this->addSesCredentials($config)
        ));
    }

    /**
     * Add the SES credentials to the configuration array.
     *
     * @param  array  $config
     * @return array
     */
    protected function addSesCredentials(array $config)
    {
        if ($config['key'] && $config['secret']) {
            $config['credentials'] = Arr::only($config, ['key', 'secret']);
        }

        return $config;
    }

    /**
     * Create an instance of the Mail Swift Transport driver.
     *
     * @return \Swift_SendmailTransport
     */
    protected function createMailDriver()
    {
        return new MailTransport;
    }

    /**
     * Create an instance of the Mailgun Swift Transport driver.
     *
     * @return \Illuminate\Mail\Transport\MailgunTransport
     */
    protected function createMailgunDriver()
    {
        $config = $this->app['config']->get('services.mailgun', []);

        return new MailgunTransport(
            $this->guzzle($config),
            $config['secret'], $config['domain']
        );
    }

    /**
     * Create an instance of the Mandrill Swift Transport driver.
     *
     * @return \Illuminate\Mail\Transport\MandrillTransport
     */
    protected function createMandrillDriver()
    {
        $config = $this->app['config']->get('services.mandrill', []);

        return new MandrillTransport(
            $this->guzzle($config), $config['secret']
        );
    }

    /**
     * Create an instance of the SparkPost Swift Transport driver.
     *
     * @return \Illuminate\Mail\Transport\SparkPostTransport
     */
    protected function createSparkPostDriver()
    {
        $config = $this->app['config']->get('services.sparkpost', []);

        return new SparkPostTransport(
            $this->guzzle($config), $config['secret'], $config['options'] ?? []
        );
    }

    /**
     * Create an instance of the Log Swift Transport driver.
     *
     * @return \Illuminate\Mail\Transport\LogTransport
     */
    protected function createLogDriver()
    {
        return new LogTransport($this->app->make(LoggerInterface::class));
    }

    /**
     * Create an instance of the Array Swift Transport Driver.
     *
     * @return \Illuminate\Mail\Transport\ArrayTransport
     */
    protected function createArrayDriver()
    {
        return new ArrayTransport;
    }

    /**
     * Get a fresh Guzzle HTTP client instance.
     *
     * @param  array  $config
     * @return \GuzzleHttp\Client
     */
    protected function guzzle($config)
    {
        return new HttpClient(Arr::add(
            $config['guzzle'] ?? [], 'connect_timeout', 60
        ));
    }

    /**
     * Get the default mail driver name.
     *
     * @return string
     */
    public function getDefaultDriver()
    {
        return $this->app['config']['mail.driver'];
    }

    /**
     * Set the default mail driver name.
     *
     * @param  string  $name
     * @return void
     */
    public function setDefaultDriver($name)
    {
        $this->app['config']['mail.driver'] = $name;
    }
}

По наблюдению видно, чтоTransportManagerВ основном для создания различных драйверов:

  • Smtp-- СоздайтеSwift_SmtpTransportЭкземпляр объекта, основные используемые параметры:host,port,encryption,username,password,stream;

  • Sendmail,Mail-- СоздайтеSwift_SendmailTransportОбъект экземпляра, используемые параметры:sendmail;

  • Ses-- СоздайтеSesTransportобъект экземпляра, используемые параметрыconfig/servicesСоответствующие значения ниже:

'ses' => [
'key' => env('SES_KEY'),
'secret' => env('SES_SECRET'),
'region' => 'us-east-1',

],


- `Mailgun` —— 创建 `MailgunTransport` 实例对象,使用的参数为 `config/services` 下对应的值:

'mailgun' => [ 'domain' => env('MAILGUN_DOMAIN'), 'secret' => env('MAILGUN_SECRET'), ],


- `Mandrill` —— 创建 `MandrillTransport` 实例对象,使用的参数为 `config/services` 下对应的值:「暂无」,可以自行添加

- `SparkPost` —— 创建 `SparkPostTransport` 实例对象,使用的参数为 `config/services` 下对应的值:

'sparkpost' => [ 'secret' => env('SPARKPOST_SECRET'), ],


此外,就是创建 `Log` 驱动,和设置默认的驱动,由 `app['config']['mail.driver']` 决定的。

Из вышеизложенного мы также можем видеть, что использованиеMailgun,MandrillилиSparkPostнужно использовать плагинguzzle, именно поэтому официальный сайт предлагает установитьguzzleПричина плагина:

В то же время эти классы водителейextends Illuminate\Mail\Transport, и абстрактный классTransportреализуетсяSwift_Transportинтерфейс:

<?php

/*
 * This file is part of SwiftMailer.
 * (c) 2004-2009 Chris Corbyn
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/**
 * Sends Messages via an abstract Transport subsystem.
 *
 * @author Chris Corbyn
 */
interface Swift_Transport
{
    /**
     * Test if this Transport mechanism has started.
     *
     * @return bool
     */
    public function isStarted();

    /**
     * Start this Transport mechanism.
     */
    public function start();

    /**
     * Stop this Transport mechanism.
     */
    public function stop();

    /**
     * Check if this Transport mechanism is alive.
     *
     * If a Transport mechanism session is no longer functional, the method
     * returns FALSE. It is the responsibility of the developer to handle this
     * case and restart the Transport mechanism manually.
     *
     * @example
     *
     *   if (!$transport->ping()) {
     *      $transport->stop();
     *      $transport->start();
     *   }
     *
     * The Transport mechanism will be started, if it is not already.
     *
     * It is undefined if the Transport mechanism attempts to restart as long as
     * the return value reflects whether the mechanism is now functional.
     *
     * @return bool TRUE if the transport is alive
     */
    public function ping();

    /**
     * Send the given Message.
     *
     * Recipient/sender data will be retrieved from the Message API.
     * The return value is the number of recipients who were accepted for delivery.
     *
     * @param Swift_Mime_SimpleMessage $message
     * @param string[]           $failedRecipients An array of failures by-reference
     *
     * @return int
     */
    public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null);

    /**
     * Register a plugin in the Transport.
     *
     * @param Swift_Events_EventListener $plugin
     */
    public function registerPlugin(Swift_Events_EventListener $plugin);
}

мы используемPhpStormПосмотрите, сколько классов реализуют интерфейс:

Ну а с экземпляром создания драйвера следующий шаг - созданиеSwift_MailerЭкземпляр объекта сейчас:

$this->app->singleton('swift.mailer', function ($app) {
    
    ...
     
    return new Swift_Mailer($app['swift.transport']->driver());
});

С помощью следующих$app['swift.transport']->driver()Поговорим о том, как получить указанный нами драйвер.

отTransportManagerродительский классManagerабстрактный класс найденdriver()функция:

/**
 * Get the default driver name.
 *
 * @return string
 */
abstract public function getDefaultDriver();

/**
 * Get a driver instance.
 *
 * @param  string  $driver
 * @return mixed
 */
public function driver($driver = null)
{
    $driver = $driver ?: $this->getDefaultDriver();

    if (is_null($driver)) {
        throw new InvalidArgumentException(sprintf(
            'Unable to resolve NULL driver for [%s].', static::class
        ));
    }

    // If the given driver has not been created before, we will create the instances
    // here and cache it so we can return it next time very quickly. If there is
    // already a driver created by this name, we'll just return that instance.
    if (! isset($this->drivers[$driver])) {
        $this->drivers[$driver] = $this->createDriver($driver);
    }

    return $this->drivers[$driver];
}

В основном используйте каждый унаследованный класс (TransportManager) осуществленный$this->getDefaultDriver()

/**
 * Get the default mail driver name.
 *
 * @return string
 */
public function getDefaultDriver()
{
    return $this->app['config']['mail.driver'];
}

Это легко понять, указанный драйвер управляетсяconfigСамоопределяемый; когда мы получаем имя драйвера, мы возвращаемся кdriver()функции, продолжайте видеть код ниже:

if (! isset($this->drivers[$driver])) {
    $this->drivers[$driver] = $this->createDriver($driver);
}

// 注:$this->createDriver($driver) 这才是真正创建指定驱动的方法

/**
 * Create a new driver instance.
 *
 * @param  string  $driver
 * @return mixed
 *
 * @throws \InvalidArgumentException
 */
protected function createDriver($driver)
{
    // We'll check to see if a creator method exists for the given driver. If not we
    // will check for a custom driver creator, which allows developers to create
    // drivers using their own customized driver creator Closure to create it.
    if (isset($this->customCreators[$driver])) {
        return $this->callCustomCreator($driver);
    } else {
        $method = 'create'.Str::studly($driver).'Driver';

        if (method_exists($this, $method)) {
            return $this->$method();
        }
    }
    throw new InvalidArgumentException("Driver [$driver] not supported.");
}

Конечно, наша цель здесь:

$method = 'create'.Str::studly($driver).'Driver';

if (method_exists($this, $method)) {
    return $this->$method();
}

Получив «имя драйвера», объединив его с именем функции, если имя нашего драйвера:mailgun, затем имя функции:createMailgunDriver, а затем вы можете напрямую выполнить этот метод, чтобы получить соответствующий экземпляр объекта драйвера.

Примечание: рекомендуется посмотреть этоStr::studly($driver)исходный код функции

На данный момент мы знаем, как использоватьconfigфайл конфигурации, чтобы создать указанный диск и, наконец, создатьSwift_Mailerобъект для последующего выполнения.

registerIlluminateMailer

Посмотрите на код:

/**
 * Register the Illuminate mailer instance.
 *
 * @return void
 */
protected function registerIlluminateMailer()
{
    $this->app->singleton('mailer', function ($app) {
        $config = $app->make('config')->get('mail');

        // 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;
    });
}

Только взгляните на это, это относительно просто, просто зайдитеview, первый шаг к созданию хорошего почтового отправителяSwift_Mailerобъект иeventsДиспетчер событий, если есть очередь, пройти в очередь, создатьIlluminate mailerОбъекты для использования в нашей реальной сцене; последнее — настроить глобальные параметры.

registerMarkdownRenderer

Laravel может захватить 💕 многих разработчиков, и есть основная часть: знать, чего хотят разработчики. Среди них Markdown в основном обязателен для разработчиков. Написание электронных писем с помощью Markdown — хорошее решение. Давайте посмотрим, как это сделать?

чтобы рубитьMarkdownкод, сначала напишите демонстрацию, чтобы увидеть, как его использовать.

использовать команду, принести--markdownОпции:

php artisan make:mail TestMdEmail --markdown=mail.testmd

Это создает для насTestMdEmailДобрый

<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;

class TestMdEmail extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->markdown('mail.testmd');
    }
}

и посмотретьtestmd.blade.php, содержимое представления по умолчанию:

@component('mail::message')
# Introduction

The body of your message.

@component('mail::button', ['url' => ''])
Button Text
@endcomponent

Thanks,<br>
{{ config('app.name') }}
@endcomponent

Напишите тест и отправьте его, чтобы увидеть, как он работает:

Artisan::command('testmd', function () {
    Mail::to('yemeishu@126.com')->send(new \App\Mail\TestMdEmail());
});

Все использует значение по умолчанию, вы можете легко создатьmarkdownотформатируйте содержимое письма и отправьте его.

Мы можем посмотреть на исходный код:

/**
 * Register the Markdown renderer instance.
 *
 * @return void
 */
protected function registerMarkdownRenderer()
{
    if ($this->app->runningInConsole()) {
        $this->publishes([
            __DIR__.'/resources/views' => $this->app->resourcePath('views/vendor/mail'),
        ], 'laravel-mail');
    }

    $this->app->singleton(Markdown::class, function ($app) {
        $config = $app->make('config');

        return new Markdown($app->make('view'), [
            'theme' => $config->get('mail.markdown.theme', 'default'),
            'paths' => $config->get('mail.markdown.paths', []),
        ]);
    });
}

Цель очень проста, использовать информацию о конфигурации, создатьMarkdownобъекта, для последующего обслуживания.

Смотрим по умолчаниюmail config:

/*
   |--------------------------------------------------------------------------
| Markdown Mail Settings
|--------------------------------------------------------------------------
|
| If you are using Markdown based email rendering, you may configure your
| theme and component paths here, allowing you to customize the design
| of the emails. Or, you may simply stick with the Laravel defaults!
|
*/

'markdown' => [
    'theme' => 'default',
    'paths' => [
        resource_path('views/vendor/mail'),
    ],
],

дефолтmarkdownинформация о конфигурации существуетviews/vendor/mailВ папке мы можем передать команду:

$ php artisan vendor:publish --tag=laravel-mail

Copied Directory [/vendor/laravel/framework/src/Illuminate/Mail/resources/views] To [/resources/views/vendor/mail]
Publishing complete.

В этой папке хранятся все компоненты по умолчанию, а также стиль оформления страницы и т. д.:

*Примечание: *Мы можем настроить компоненты и увеличить почтовый ящик выпуска.cssстиль

СмотретьMaikdownКонструктор:

/**
 * Create a new Markdown renderer instance.
 *
 * @param  \Illuminate\Contracts\View\Factory  $view
 * @param  array  $options
 * @return void
 */
public function __construct(ViewFactory $view, array $options = [])
{
    $this->view = $view;
    $this->theme = $options['theme'] ?? 'default';
    $this->loadComponentsFrom($options['paths'] ?? []);
}

в основном входящиеViewпросмотр конструктора и стилей тем, а также отдельныхmarkdownкомпоненты.

Процесс отправки почты

Давайте объединим приведенную выше демонстрацию, чтобы увидеть, как создать содержимое электронного письма, и чтобы отправить электронное письмо, давайте посмотрим на код:

Mail::to('yemeishu@126.com')->send(new \App\Mail\TestMdEmail());

здесьMailчуть вышеregisterIlluminateMailerЗарегистрированоIlluminate\Mail\Mailerобъект.

давай увидим этоsend()метод:

/**
 * Send a new message using a view.
 *
 * @param  string|array|MailableContract  $view
 * @param  array  $data
 * @param  \Closure|string  $callback
 * @return void
 */
public function send($view, array $data = [], $callback = null)
{
    if ($view instanceof MailableContract) {
        return $this->sendMailable($view);
    }

    // First we need to parse the view, which could either be a string or an array
    // containing both an HTML and plain text versions of the view which should
    // be used when sending an e-mail. We will extract both of them out here.
    list($view, $plain, $raw) = $this->parseView($view);

    $data['message'] = $message = $this->createMessage();

    // Once we have retrieved the view content for the e-mail we will set the body
    // of this message using the HTML type, which will provide a simple wrapper
    // to creating view based emails that are able to receive arrays of data.
    call_user_func($callback, $message);

    $this->addContent($message, $view, $plain, $raw, $data);

    // If a global "to" address has been set, we will set that address on the mail
    // message. This is primarily useful during local development in which each
    // message should be delivered into a single mail address for inspection.
    if (isset($this->to['address'])) {
        $this->setGlobalTo($message);
    }

    // Next we will determine if the message should be sent. We give the developer
    // one final chance to stop this message and then we will send it to all of
    // its recipients. We will then fire the sent event for the sent message.
    $swiftMessage = $message->getSwiftMessage();

    if ($this->shouldSendMessage($swiftMessage, $data)) {
        $this->sendSwiftMessage($swiftMessage);

        $this->dispatchSentEvent($message, $data);
    }
}

Давайте посмотрим на первый шаг:

if ($view instanceof MailableContract) {
    return $this->sendMailable($view);
}

реализовано$this->sendMailable($view):

/**
 * Send the message using the given mailer.
 *
 * @param  \Illuminate\Contracts\Mail\Mailer  $mailer
 * @return void
 */
public function send(MailerContract $mailer)
{
    $translator = Container::getInstance()->make(Translator::class);

    $this->withLocale($this->locale, $translator, function () use ($mailer) {
        Container::getInstance()->call([$this, 'build']);

        $mailer->send($this->buildView(), $this->buildViewData(), function ($message) {
            $this->buildFrom($message)
                 ->buildRecipients($message)
                 ->buildSubject($message)
                 ->runCallbacks($message)
                 ->buildAttachments($message);
        });
    });
}

Суть в том, чтобы сначала выполнить наш дефолтbuildметод:

/**
 * Build the message.
 *
 * @return $this
 */
public function build()
{
    return $this->markdown('mail.testmd');
}

Вот почему, когда команда создает класс шаблона для отправки электронных писем, он будет создан по умолчанию.buildметод, а затем в этом методе загрузите содержимое и логику нашей сборки; вmarkdownпредставление, по умолчанию выполняется$this->markdown('mail.testmd'):

/**
 * Set the Markdown template for the message.
 *
 * @param  string  $view
 * @param  array  $data
 * @return $this
 */
public function markdown($view, array $data = [])
{
    $this->markdown = $view;
    $this->viewData = array_merge($this->viewData, $data);

    return $this;
}

Загрузите представление и содержимое представления в объект.

Затем возвращаемся к предыдущемуsendВ методе:

$mailer->send($this->buildView(), $this->buildViewData(), function ($message) {
    $this->buildFrom($message)
         ->buildRecipients($message)
         ->buildSubject($message)
         ->runCallbacks($message)
         ->buildAttachments($message);
});

Давайте разберем его один за другим:

$this->buildView()

/**
 * Build the view for the message.
 *
 * @return array|string
 */
protected function buildView()
{
    if (isset($this->html)) {
        return array_filter([
            'html' => new HtmlString($this->html),
            'text' => isset($this->textView) ? $this->textView : null,
        ]);
    }

    if (isset($this->markdown)) {
        return $this->buildMarkdownView();
    }

    if (isset($this->view, $this->textView)) {
        return [$this->view, $this->textView];
    } elseif (isset($this->textView)) {
        return ['text' => $this->textView];
    }

    return $this->view;
}

Очевидно, выполнить$this->buildMarkdownView()

/**
 * Build the Markdown view for the message.
 *
 * @return array
 */
protected function buildMarkdownView()
{
    $markdown = Container::getInstance()->make(Markdown::class);

    if (isset($this->theme)) {
        $markdown->theme($this->theme);
    }

    $data = $this->buildViewData();

    return [
        'html' => $markdown->render($this->markdown, $data),
        'text' => $this->buildMarkdownText($markdown, $data),
    ];
}

В этот момент,MarkdownОбъекты пригодятся, и цель должна быть на этих двух методах:

return [
    'html' => $markdown->render($this->markdown, $data),
    'text' => $this->buildMarkdownText($markdown, $data),
];

Смотреть$markdown->render()метод:

/**
 * Render the Markdown template into HTML.
 *
 * @param  string  $view
 * @param  array  $data
 * @param  \TijsVerkoyen\CssToInlineStyles\CssToInlineStyles|null  $inliner
 * @return \Illuminate\Support\HtmlString
 */
public function render($view, array $data = [], $inliner = null)
{
    $this->view->flushFinderCache();

    $contents = $this->view->replaceNamespace(
        'mail', $this->htmlComponentPaths()
    )->make($view, $data)->render();

    return new HtmlString(($inliner ?: new CssToInlineStyles)->convert(
        $contents, $this->view->make('mail::themes.'.$this->theme)->render()
    ));
}

а также$markdown->renderText()метод:


/**
 * Render the Markdown template into HTML.
 *
 * @param  string  $view
 * @param  array  $data
 * @return \Illuminate\Support\HtmlString
 */
public function renderText($view, array $data = [])
{
    $this->view->flushFinderCache();

    $contents = $this->view->replaceNamespace(
        'mail', $this->markdownComponentPaths()
    )->make($view, $data)->render();

    return new HtmlString(
        html_entity_decode(preg_replace("/[\r\n]{2,}/", "\n\n", $contents), ENT_QUOTES, 'UTF-8')
    );
}

Основная логика состоит в том, чтобыmarkdownформатировать вhtmlформат и формирование массива['html', 'data']вывод и, наконец, выполнить сноваsendметод и передать функцию закрытия для построенияmessageСлужить:

$mailer->send($this->buildView(), $this->buildViewData(), function ($message) {
    $this->buildFrom($message)
         ->buildRecipients($message)
         ->buildSubject($message)
         ->runCallbacks($message)
         ->buildAttachments($message);
});

давайте оглянемся назадsendметод, неразрешенный код:

// First we need to parse the view, which could either be a string or an array
    // containing both an HTML and plain text versions of the view which should
    // be used when sending an e-mail. We will extract both of them out here.
    list($view, $plain, $raw) = $this->parseView($view);

    $data['message'] = $message = $this->createMessage();

    // Once we have retrieved the view content for the e-mail we will set the body
    // of this message using the HTML type, which will provide a simple wrapper
    // to creating view based emails that are able to receive arrays of data.
    call_user_func($callback, $message);

    $this->addContent($message, $view, $plain, $raw, $data);

    // If a global "to" address has been set, we will set that address on the mail
    // message. This is primarily useful during local development in which each
    // message should be delivered into a single mail address for inspection.
    if (isset($this->to['address'])) {
        $this->setGlobalTo($message);
    }

    // Next we will determine if the message should be sent. We give the developer
    // one final chance to stop this message and then we will send it to all of
    // its recipients. We will then fire the sent event for the sent message.
    $swiftMessage = $message->getSwiftMessage();

    if ($this->shouldSendMessage($swiftMessage, $data)) {
        $this->sendSwiftMessage($swiftMessage);

        $this->dispatchSentEvent($message, $data);
    }

Первый шаг - это не что иное, как пересечь вышеуказанный массив, а затем создатьMessageОбъект:

$data['message'] = $message = $this->createMessage();

/**
 * Create a new message instance.
 *
 * @return \Illuminate\Mail\Message
 */
protected function createMessage()
{
    $message = new Message($this->swift->createMessage('message'));

    // If a global from address has been specified we will set it on every message
    // instance so the developer does not have to repeat themselves every time
    // they create a new message. We'll just go ahead and push this address.
    if (! empty($this->from['address'])) {
        $message->from($this->from['address'], $this->from['name']);
    }

    // When a global reply address was specified we will set this on every message
    // instance so the developer does not have to repeat themselves every time
    // they create a new message. We will just go ahead and push this address.
    if (! empty($this->replyTo['address'])) {
        $message->replyTo($this->replyTo['address'], $this->replyTo['name']);
    }

    return $message;
}

этоMessageКонструктор прошел вswiftСервисный объект, позжеmessageВходящие данные передаютсяswiftсервисный объект.

$message = new Message($this->swift->createMessage('message'));

...

/**
 * Create a new class instance of one of the message services.
 *
 * For example 'mimepart' would create a 'message.mimepart' instance
 *
 * @param string $service
 *
 * @return object
 */
public function createMessage($service = 'message')
{
    return Swift_DependencyContainer::getInstance()
        ->lookup('message.'.$service);
}

Такие как:

/**
 * Add a "from" address to the message.
 *
 * @param  string|array  $address
 * @param  string|null  $name
 * @return $this
 */
public function from($address, $name = null)
{
    $this->swift->setFrom($address, $name);

    return $this;
}

/**
 * Set the "sender" of the message.
 *
 * @param  string|array  $address
 * @param  string|null  $name
 * @return $this
 */
public function sender($address, $name = null)
{
    $this->swift->setSender($address, $name);

    return $this;
}

Итак, начинаем использоватьMailServiceProviderсоздан вSwift_Mailerобъект.

Ну и наконец последний шаг:

// Next we will determine if the message should be sent. We give the developer
    // one final chance to stop this message and then we will send it to all of
    // its recipients. We will then fire the sent event for the sent message.
    $swiftMessage = $message->getSwiftMessage();

    if ($this->shouldSendMessage($swiftMessage, $data)) {
        $this->sendSwiftMessage($swiftMessage);

        $this->dispatchSentEvent($message, $data);
    }

ПолучатьswiftЗатем объект службы начинает выполнять логику отправки и отправляет событие отправки почты.

/**
 * Send a Swift Message instance.
 *
 * @param  \Swift_Message  $message
 * @return void
 */
protected function sendSwiftMessage($message)
{
    try {
        return $this->swift->send($message, $this->failedRecipients);
    } finally {
        $this->forceReconnection();
    }
}

...

/**
 * Dispatch the message sent event.
 *
 * @param  \Illuminate\Mail\Message  $message
 * @param  array  $data
 * @return void
 */
protected function dispatchSentEvent($message, $data = [])
{
    if ($this->events) {
        $this->events->dispatch(
            new Events\MessageSent($message->getSwiftMessage(), $data)
        );
    }
}

Продолжайте видеть, как использоватьswiftобъект для отправки почты.

/**
 * Send a Swift Message instance.
 *
 * @param  \Swift_Message  $message
 * @return void
 */
protected function sendSwiftMessage($message)
{
    try {
        return $this->swift->send($message, $this->failedRecipients);
    } finally {
        $this->forceReconnection();
    }
}

Смотреть$this->swift->send()метод:

/**
 * Send the given Message like it would be sent in a mail client.
 *
 * All recipients (with the exception of Bcc) will be able to see the other
 * recipients this message was sent to.
 *
 * Recipient/sender data will be retrieved from the Message object.
 *
 * The return value is the number of recipients who were accepted for
 * delivery.
 *
 * @param Swift_Mime_SimpleMessage $message
 * @param array                    $failedRecipients An array of failures by-reference
 *
 * @return int The number of successful recipients. Can be 0 which indicates failure
 */
public function send(Swift_Mime_SimpleMessage $message, &$failedRecipients = null)
{
    $failedRecipients = (array) $failedRecipients;

    if (!$this->transport->isStarted()) {
        $this->transport->start();
    }

    $sent = 0;

    try {
        $sent = $this->transport->send($message, $failedRecipients);
    } catch (Swift_RfcComplianceException $e) {
        foreach ($message->getTo() as $address => $name) {
            $failedRecipients[] = $address;
        }
    }

    return $sent;
}

Не забудьте инкапсулировать каждый драйвер отправки в начале,sendНаконец, действие передается нашему драйверу отправки почты для выполнения.SmtpTransport,СейчасSwift_SmtpTransportОтправить.

$sent = $this->transport->send($message, $failedRecipients);

Суммировать

После изучения кода у меня появилось приблизительное представление о том, как инкапсулировать каждый драйвер.markdownотформатировать вhtmlформате, а затем инкапсулируется вMessageобъект, и передать его водителю для отправки почты.

Давайте поговорим о следующем шагеSwift_SmtpTransportПринцип реализации, и как мы сами делаем драйвер, и, наконец, какие шаблоны проектирования используются в этом процессе?

Продолжение следует