Sending mail
A site sends emails: a registration confirmation, a notice about a new comment, a message from the contact form. In Yii the Yii::$app->mailer component is responsible for this. In the standard project it works through the yiisoft/yii2-symfonymailer package, which is already installed.
Where mail is configured
The component is described in config/web.php:
// config/web.php
'components' => [
'mailer' => [
'class' => \yii\symfonymailer\Mailer::class,
'viewPath' => '@app/mail',
'useFileTransport' => true,
],
],While useFileTransport is true, emails go nowhere and are saved as files in the runtime/mail folder. This is the development mode: you can check emails without the risk of sending them to real people.
The first email
// controllers/PostController.php
public function actionNotify(): string
{
$sent = Yii::$app->mailer->compose()
->setFrom('blog@example.com')
->setTo('anna@example.com')
->setSubject('A new post in the blog')
->setTextBody('A new post has appeared in the blog.')
->send();
return $sent ? 'Email sent' : 'Sending failed';
}Open /post/notify and a file with the .eml extension appears in the runtime/mail folder. A mail program or a text editor opens it. The send() method returns true on success.
An email from a view
Email views live in the mail folder. Create mail/new-post.php:
<?php
// mail/new-post.php
use yii\helpers\Html;
use yii\helpers\Url;
?>
<h1><?= Html::encode($post->title) ?></h1>
<p>A new post has appeared in the blog.</p>
<p><?= Html::a('Read', Url::to(['post/view', 'id' => $post->id], true)) ?></p>The view name and the data are passed to compose():
Yii::$app->mailer->compose('new-post', ['post' => $post])
->setFrom('blog@example.com')
->setTo('anna@example.com')
->setSubject('New post: ' . $post->title)
->send();The email gets the markup from the view, wrapped in the common email layout mail/layouts/html.php. The second argument of Url::to(), true, makes the URL absolute: a relative link /posts/5 does not work in an email.
Message methods
| Method | What it sets |
|---|---|
setFrom('blog@example.com') | The sender |
setTo(['anna@example.com', 'ivan@example.com']) | One or several recipients |
setSubject('Subject') | The subject |
setTextBody('Text') | Plain text content |
setHtmlBody('<b>Text</b>') | Content with markup |
attach('/path/to/file.pdf') | An attachment |
Real sending
On a server the file mode is switched off and a mail server is specified:
'mailer' => [
'class' => \yii\symfonymailer\Mailer::class,
'viewPath' => '@app/mail',
'useFileTransport' => false,
'transport' => [
'dsn' => 'smtp://user:password@smtp.example.com:587',
],
],The dsn string contains the user name, the password, the address and the port of the SMTP server; the mail service provides them.
Common mistake
Emails "do not arrive" although send() returns true: this means useFileTransport is still on, and they pile up in runtime/mail. Check this setting on the server. The opposite mistake is more dangerous: switch it off on a developer's computer with a copy of the real database, and test emails go to real users.