邮件程序

class Cake\Mailer\Mailer(string|array|null $profile = null)

Mailer 是一个用于发送电子邮件的便捷类。使用此类,您可以在应用程序的任何地方发送电子邮件。

基本用法

首先,您应该确保该类已加载

use Cake\Mailer\Mailer;

加载 Mailer 后,您可以使用以下方法发送电子邮件

$mailer = new Mailer('default');
$mailer->setFrom(['[email protected]' => 'My Site'])
    ->setTo('[email protected]')
    ->setSubject('About')
    ->deliver('My message');

由于 Mailer 的 setter 方法返回类实例,因此您可以使用方法链设置其属性。

Mailer 有几种方法可以定义收件人 - setTo()setCc()setBcc()addTo()addCc()addBcc()。主要区别在于前三个将覆盖已设置的内容,而后者只会将更多收件人添加到其各自的字段

$mailer = new Mailer();
$mailer->setTo('[email protected]', 'To Example');
$mailer->addTo('[email protected]', 'To2 Example');
// The email's To recipients are: [email protected] and [email protected]
$mailer->setTo('[email protected]', 'ToTest Example');
// The email's To recipient is: [email protected]

选择发件人

以他人名义发送电子邮件时,最好使用 Sender 标头定义原始发件人。您可以使用 setSender() 来做到这一点

$mailer = new Mailer();
$mailer->setSender('[email protected]', 'MyApp emailer');

注意

以他人名义发送邮件时,最好也设置信封发件人。这可以防止他们收到有关可交付性的任何消息。

配置

邮件程序配置文件和电子邮件传输设置在您的应用程序的配置文件中定义。EmailEmailTransport 键分别定义邮件程序配置文件和电子邮件传输配置。在应用程序引导期间,配置设置会通过 Configure 使用 setConfig() 传递到 MailerTransportFactory 类中。通过定义配置文件和传输,您可以使您的应用程序代码不包含配置数据,并避免重复,从而使维护和部署更加容易。

要加载预定义的配置,可以使用 setProfile() 方法或将其传递给 Mailer 的构造函数

$mailer = new Mailer();
$mailer->setProfile('default');

// Or in constructor
$mailer = new Mailer('default');

除了传递与预设配置名称匹配的字符串外,您还可以直接加载一个选项数组

$mailer = new Mailer();
$mailer->setProfile(['from' => '[email protected]', 'transport' => 'my_custom']);

// Or in constructor
$mailer = new Mailer(['from' => '[email protected]', 'transport' => 'my_custom']);

配置配置文件

定义传递配置文件允许您将常见电子邮件设置整合到可重复使用的配置文件中。您的应用程序可以拥有任意数量的配置文件。以下配置键将被使用

  • 'from': 邮件程序或发件人数组。参见 Mailer::setFrom()

  • 'sender': 邮件程序或真实发件人数组。参见 Mailer::setSender()

  • 'to': 邮件程序或目标数组。参见 Mailer::setTo()

  • 'cc': 邮件程序或抄送数组。参见 Mailer::setCc()

  • 'bcc': 邮件程序或密件抄送数组。参见 Mailer::setBcc()

  • 'replyTo': 邮件程序或回复电子邮件的数组。参见 Mailer::setReplyTo()

  • 'readReceipt': 邮件程序地址或接收已读回执的地址数组。参见 Mailer::setReadReceipt()

  • 'returnPath': 邮件程序地址或出现错误时返回的地址数组。参见 Mailer::setReturnPath()

  • 'messageId': 电子邮件的消息 ID。参见 Mailer::setMessageId()

  • 'subject': 消息的主题。参见 Mailer::setSubject()

  • 'message': 消息的内容。如果您使用的是渲染内容,请不要设置此字段。

  • 'priority': 邮件的优先级,以数字形式表示(通常从 1 到 5,其中 1 为最高)。

  • 'headers': 要包含的标题。参见 Mailer::setHeaders()

  • 'viewRenderer': 如果您使用的是渲染内容,请设置视图类名。参见 ViewBuilder::setClassName()

  • 'template': 如果您使用的是渲染内容,请设置模板名称。参见 ViewBuilder::setTemplate()

  • 'theme': 渲染模板时使用的主题。参见 ViewBuilder::setTheme()

  • 'layout': 如果您使用的是渲染内容,请设置要渲染的布局。参见 ViewBuilder::setTemplate()

  • 'autoLayout': 如果您想在不使用布局的情况下渲染模板,请将此字段设置为 false。参见 ViewBuilder::disableAutoLayout()

  • 'viewVars': 如果您使用的是渲染内容,请设置包含要在视图中使用的变量的数组。参见 Mailer::setViewVars()

  • 'attachments': 要附加的文件列表。参见 Mailer::setAttachments()

  • 'emailFormat': 电子邮件格式(html、text 或两者)。参见 Mailer::setEmailFormat()

  • 'transport': 传输配置名称。参见 配置传输.

  • 'log': 用于记录电子邮件标题和消息的日志级别。true 将使用 LOG_DEBUG。参见 使用级别。请注意,日志将在名为 email 的范围内发出。另请参见 记录范围

  • 'helpers': 邮件模板中使用的帮助器数组。 ViewBuilder::setHelpers()/ViewBuilder::addHelpers()

注意

上述键的值使用 Mailer 或数组,例如 from、to、cc 等,将作为对应方法的第一个参数传递。 等效于:$mailer->setFrom('[email protected]', 'My Site') 将定义为 'from' => ['[email protected]' => 'My Site'] 在您的配置中

设置 Headers

Mailer 中,您可以自由设置所需的任何 Headers。 不要忘记为您的自定义 Headers 添加 X- 前缀。

参见 Mailer::setHeaders()Mailer::addHeaders()

发送模板邮件

邮件通常不仅仅是一条简单的文本消息。 为了方便起见,CakePHP 提供了一种使用 CakePHP 的 视图层 发送邮件的方法。

邮件模板位于应用程序的特殊文件夹 templates/email 中。 Mailer 视图也可以使用布局和元素,就像普通的视图一样

$mailer = new Mailer();
$mailer
            ->setEmailFormat('html')
            ->setTo('[email protected]')
            ->setFrom('[email protected]')
            ->viewBuilder()
                ->setTemplate('welcome')
                ->setLayout('fancy');

$mailer->deliver();

以上将使用 **templates/email/html/welcome.php** 作为视图,以及 **templates/layout/email/html/fancy.php** 作为布局。 您也可以发送多部分模板邮件

$mailer = new Mailer();
$mailer
            ->setEmailFormat('both')
            ->setTo('[email protected]')
            ->setFrom('[email protected]')
            ->viewBuilder()
                ->setTemplate('welcome')
                ->setLayout('fancy');

$mailer->deliver();

这将使用以下模板文件

  • templates/email/text/welcome.php

  • templates/layout/email/text/fancy.php

  • templates/email/html/welcome.php

  • templates/layout/email/html/fancy.php

发送模板邮件时,您可以选择发送 texthtmlboth

您可以使用 Mailer::viewBuilder() 获取的视图构建器实例设置所有与视图相关的配置,类似于您在控制器中执行的操作。

您可以使用 Mailer::setViewVars() 设置视图变量

$mailer = new Mailer('templated');
$mailer->setViewVars(['value' => 12345]);

或者您可以使用视图构建器方法 ViewBuilder::setVar()ViewBuilder::setVars()

在您的邮件模板中,您可以使用它们

<p>Here is your value: <b><?= $value ?></b></p>

您也可以在邮件中使用帮助器,就像您在普通的模板文件中一样。 默认情况下,只加载 HtmlHelper。 您可以使用 ViewBuilder::addHelpers() 方法加载其他帮助器

$mailer->viewBuilder()->addHelpers(['Html', 'Custom', 'Text']);

添加帮助器时,请确保包含 'Html',否则它将从邮件模板中加载的帮助器中删除。

注意

在 4.3.0 之前的版本中,您需要使用 setHelpers()

如果您想使用插件中的模板发送邮件,可以使用熟悉的 插件语法 来执行此操作

$mailer = new Mailer();
$mailer->viewBuilder()->setTemplate('Blog.new_comment');

以上将使用来自 Blog 插件的模板和布局作为示例。

在某些情况下,您可能需要覆盖插件提供的默认模板。 您可以使用主题来做到这一点

$mailer->viewBuilder()
    ->setTemplate('Blog.new_comment')
    ->setLayout('Blog.auto_message')
    ->setTheme('TestTheme');

这允许您覆盖主题中的 new_comment 模板,而无需修改 Blog 插件。 模板文件需要在以下路径中创建: **templates/plugin/TestTheme/plugin/Blog/email/text/new_comment.php**。

发送附件

Cake\Mailer\Mailer::setAttachments($attachments)

您也可以将文件附加到邮件中。 根据您拥有的文件类型以及希望在收件人邮件客户端中显示的文件名,有几种不同的格式

  1. 数组:$mailer->setAttachments(['/full/file/path/file.png']) 将以 file.png 的名称附加此文件。

  2. 带键的数组:$mailer->setAttachments(['photo.png' => '/full/some_hash.png']) 将以 photo.png 的名称附加 some_hash.png。 收件人将看到 photo.png,而不是 some_hash.png。

  3. 嵌套数组

    $mailer->setAttachments([
        'photo.png' => [
            'file' => '/full/some_hash.png',
            'mimetype' => 'image/png',
            'contentId' => 'my-unique-id',
        ],
    ]);
    

    以上将以不同的 mimetype 和自定义 Content ID 附加文件(设置 Content ID 后,附件将转换为内联)。 在这种形式下,mimetype 和 contentId 是可选的。

    3.1. 当您使用 contentId 时,您可以在 HTML 主体中使用该文件,例如 <img src="cid:my-content-id">

    3.2. 您可以使用 contentDisposition 选项来禁用附件的 Content-Disposition 标题。 当使用 Outlook 向客户端发送 ical 邀请时,这很有用。

    3.3. 而不是 file 选项,您可以使用 data 选项提供文件内容作为字符串。 这使您可以附加文件,而无需它们的路径。

放宽地址验证规则

Cake\Mailer\Mailer::setEmailPattern($pattern)

如果您在向不兼容地址发送邮件时遇到验证问题,您可以放宽用于验证电子邮件地址的模式。 这在处理某些 ISP 时有时是必要的

$mailer = new Mailer('default');

// Relax the email pattern, so you can send
// to non-conformant addresses.
$mailer->setEmailPattern($newPattern);

从 CLI 发送邮件

在 CLI 脚本(Shell、Task 等)中发送邮件时,您应该手动设置 Mailer 要使用的域名。 它将作为消息 ID 的主机名(因为 CLI 环境中没有主机名)

$mailer->setDomain('www.example.org');
// Results in message ids like ``<[email protected]>`` (valid)
// Instead of `<UUID@>`` (invalid)

有效的邮件 ID 可以帮助防止邮件被送入垃圾邮件文件夹。

创建可重用邮件

到目前为止,我们已经了解了如何直接使用 Mailer 类来创建和发送一封邮件。 但是 Mailer 的主要功能是允许在整个应用程序中创建可重用邮件。 它们也可以用来在一个位置包含多个邮件配置。 这有助于使代码更 DRY,并将邮件配置噪声从应用程序中的其他区域剔除。

在这个例子中,我们将创建一个包含用户相关邮件的 Mailer。 要创建我们的 UserMailer,请创建文件 **src/Mailer/UserMailer.php**。 文件内容应如下所示

namespace App\Mailer;

use Cake\Mailer\Mailer;

class UserMailer extends Mailer
{
    public function welcome($user)
    {
        $this
            ->setTo($user->email)
            ->setSubject(sprintf('Welcome %s', $user->name))
            ->viewBuilder()
                ->setTemplate('welcome_mail'); // By default template with same name as method name is used.
    }

    public function resetPassword($user)
    {
        $this
            ->setTo($user->email)
            ->setSubject('Reset password')
            ->setViewVars(['token' => $user->token]);
    }
}

在我们的例子中,我们创建了两个方法,一个用于发送欢迎邮件,另一个用于发送密码重置邮件。 这些方法中的每一个都期望一个用户 Entity,并使用它的属性来配置每封邮件。

我们现在能够使用我们的 UserMailer 从应用程序中的任何位置发送我们的用户相关邮件。 例如,如果我们想发送欢迎邮件,我们可以执行以下操作

namespace App\Controller;

use Cake\Mailer\MailerAwareTrait;

class UsersController extends AppController
{
    use MailerAwareTrait;

    public function register()
    {
        $user = $this->Users->newEmptyEntity();
        if ($this->request->is('post')) {
            $user = $this->Users->patchEntity($user, $this->request->getData())
            if ($this->Users->save($user)) {
                $this->getMailer('User')->send('welcome', [$user]);
            }
        }
        $this->set('user', $user);
    }
}

如果我们想完全将发送用户欢迎邮件与应用程序的代码分离,我们可以让我们的 UserMailer 订阅 Model.afterSave 事件。 通过订阅事件,我们可以使应用程序的用户相关类完全没有与邮件相关的逻辑和指令。 例如,我们可以将以下内容添加到我们的 UserMailer

public function implementedEvents()
{
    return [
        'Model.afterSave' => 'onRegistration',
    ];
}

public function onRegistration(EventInterface $event, EntityInterface $entity, ArrayObject $options)
{
    if ($entity->isNew()) {
        $this->send('welcome', [$entity]);
    }
}

您现在可以将 Mailer 注册为事件监听器,并且每次触发 Model.afterSave 事件时,都会调用 onRegistration() 方法

// attach to Users event manager
$this->Users->getEventManager()->on($this->getMailer('User'));

注意

有关如何注册事件监听器对象的详细信息,请参阅 注册监听器 文档。

配置传输

邮件由传输发送。 不同的传输允许您通过 PHP 的 mail() 函数、SMTP 服务器发送邮件,或者根本不发送,这对于调试很有用。 配置传输允许您将配置数据保留在应用程序代码之外,并简化部署,因为您只需更改配置数据。 传输配置示例如下

// In config/app.php
'EmailTransport' => [
    // Sample Mail configuration
    'default' => [
        'className' => 'Mail',
    ],
    // Sample SMTP configuration
    'gmail' => [
        'host' => 'smtp.gmail.com',
        'port' => 587,
        'username' => '[email protected]',
        'password' => 'secret',
        'className' => 'Smtp',
        'tls' => true,
    ],
],

传输也可以使用 TransportFactory::setConfig() 在运行时配置

use Cake\Mailer\TransportFactory;

// Define an SMTP transport
TransportFactory::setConfig('gmail', [
    'host' => 'ssl://smtp.gmail.com',
    'port' => 465,
    'username' => '[email protected]',
    'password' => 'secret',
    'className' => 'Smtp'
]);

您可以配置 SSL SMTP 服务器,例如 Gmail。 为此,请在主机中添加 ssl:// 前缀,并相应地配置端口值。 您也可以使用 tls 选项启用 TLS SMTP

use Cake\Mailer\TransportFactory;

TransportFactory::setConfig('gmail', [
    'host' => 'smtp.gmail.com',
    'port' => 587,
    'username' => '[email protected]',
    'password' => 'secret',
    'className' => 'Smtp',
    'tls' => true
]);

以上配置将为邮件启用 TLS 通信。

要配置您的 Mailer 以使用特定传输,您可以使用 Cake\Mailer\Mailer::setTransport() 方法,或者在您的配置中包含传输

// Use a named transport already configured using TransportFactory::setConfig()
$mailer->setTransport('gmail');

// Use a constructed object.
$mailer->setTransport(new \Cake\Mailer\Transport\DebugTransport());

警告

要使此操作正常工作,您需要在 Google 帐户中启用对安全性较低的应用程序的访问:允许安全性较低的应用程序访问您的帐户.

注意

要使用 SSL + SMTP,您需要在您的 PHP 安装中配置 SSL。

配置选项也可以作为 DSN 字符串提供。 这在使用环境变量或 PaaS 提供商时很有用

TransportFactory::setConfig('default', [
    'url' => 'smtp://[email protected]:[email protected]:587?tls=true',
]);

当使用 DSN 字符串时,可以将任何其他参数/选项定义为查询字符串参数。

static Cake\Mailer\Mailer::drop($key)

配置后,无法修改传输。要修改传输,必须先将其删除,然后重新配置。

创建自定义传输

您可以为使用 SendGrid、MailGun 或 Postmark 等服务发送电子邮件的情况创建自定义传输。要创建传输,首先创建文件 **src/Mailer/Transport/ExampleTransport.php**(其中 Example 是您传输的名称)。首先,您的文件应如下所示

namespace App\Mailer\Transport;

use Cake\Mailer\AbstractTransport;
use Cake\Mailer\Message;

class ExampleTransport extends AbstractTransport
{
    public function send(Message $message): array
    {
        // Do something.
    }
}

您必须使用自定义逻辑实现方法 send(Message $message)

不使用 Mailer 发送电子邮件

Mailer 是一个更高级别的抽象类,它充当 Cake\Mailer\MessageCake\Mailer\RendererCake\Mailer\AbstractTransport 类之间的桥梁,以使用流利的界面配置电子邮件。

如果需要,也可以直接使用 Mailer 这些类。

例如

$render = new \Cake\Mailer\Renderer();
$render->viewBuilder()
    ->setTemplate('custom')
    ->setLayout('sparkly');

$message = new \Cake\Mailer\Message();
$message
    ->setFrom('[email protected]')
    ->setTo('[email protected]')
    ->setBody($render->render());

$transport = new \Cake\Mailer\Transport\MailTransport();
$result = $transport->send($message);

您甚至可以跳过使用 Renderer,并使用 Message::setBodyText()Message::setBodyHtml() 方法直接设置邮件正文。

测试邮件程序

要测试邮件程序,请将 Cake\TestSuite\EmailTrait 添加到您的测试用例中。 MailerTrait 使用 PHPUnit 钩子替换应用程序的电子邮件传输,用代理替换,代理拦截电子邮件,并允许您对将要传送的邮件进行断言。

将特性添加到您的测试用例中以开始测试电子邮件,并在您的电子邮件需要生成 URL 时加载路由

namespace App\Test\TestCase\Mailer;

use App\Mailer\WelcomeMailer;
use App\Model\Entity\User;

use Cake\TestSuite\EmailTrait;
use Cake\TestSuite\TestCase;

class WelcomeMailerTestCase extends TestCase
{
    use EmailTrait;

    public function setUp(): void
    {
        parent::setUp();
        $this->loadRoutes();
    }
}

假设我们有一个邮件程序,在注册新用户时发送欢迎电子邮件。我们要检查主题和正文是否包含用户姓名

// in our WelcomeMailerTestCase class.
public function testName()
{
    $user = new User([
        'name' => 'Alice Alittea',
        'email' => '[email protected]',
    ]);
    $mailer = new WelcomeMailer();
    $mailer->send('welcome', [$user]);

    $this->assertMailSentTo($user->email);
    $this->assertMailContainsText('Hi ' . $user->name);
    $this->assertMailContainsText('Welcome to CakePHP!');
}

断言方法

Cake\TestSuite\EmailTrait 特性提供以下断言

// Asserts an expected number of emails were sent
$this->assertMailCount($count);

// Asserts that no emails were sent
$this->assertNoMailSent();

// Asserts an email was sent to an address
$this->assertMailSentTo($address);

// Asserts an email was sent from an address
$this->assertMailSentFrom($emailAddress);
$this->assertMailSentFrom([$emailAddress => $displayName]);

// Asserts an email contains expected contents
$this->assertMailContains($contents);

// Asserts an email contains expected html contents
$this->assertMailContainsHtml($contents);

// Asserts an email contains expected text contents
$this->assertMailContainsText($contents);

// Asserts an email contains the expected value within an Message getter (for example, "subject")
$this->assertMailSentWith($expected, $parameter);

// Asserts an email at a specific index was sent to an address
$this->assertMailSentToAt($at, $address);

// Asserts an email at a specific index was sent from an address
$this->assertMailSentFromAt($at, $address);

// Asserts an email at a specific index contains expected contents
$this->assertMailContainsAt($at, $contents);

// Asserts an email at a specific index contains expected html contents
$this->assertMailContainsHtmlAt($at, $contents);

// Asserts an email at a specific index contains expected text contents
$this->assertMailContainsTextAt($at, $contents);

// Asserts an email contains an attachment
$this->assertMailContainsAttachment('test.png');

// Asserts an email at a specific index contains the expected value within an Message getter (for example, "cc")
$this->assertMailSentWithAt($at, $expected, $parameter);

// Asserts an email contains a substring in the subject.
$this->assertMailSubjectContains('Free Offer');

// Asserts an email at the specific index contains a substring in the subject.
$this->assertMailSubjectContainsAt(1, 'Free Offer');