Yii2. Отправка почты

14.08.2019

Теги: Web-разработкаYii2ФормаФреймворк

Для отправки почты Yii2 предлагает расширение swiftmailer, которое нужно просто настроить. Настраивается оно в файле config/web.php. Вот настройки класса swiftmailer по умолчанию:

$config = [
    /*...*/,
    'components' => [
        /*...*/
        'mailer' => [
            'class' => 'yii\swiftmailer\Mailer',
            // send all mails to a file by default. You have to set
            // 'useFileTransport' to false and configure a transport
            // for the mailer to send real emails.
            'useFileTransport' => true,
        ],
        /*...*/
    ],
    /*...*/
];

Обратите внимание на настройку useFileTransport: когда она имеет значение true — письмо не отправляется реально, его отправка просто эмулируется. Файл письма при этом будет сохранен в папку runtime/mail.

Тестирование отправки почты

Давайте протестируем отправку почты. Для этого создадим форму обратной связи, которая будет доступна на адресу /site/feedback.

<?php
namespace app\controllers;

use Yii;
use app\models\Feedback;
use yii\web\Controller;

class SiteController extends Controller {

    /*...*/

    public function actionFeedback() {
        $model = new Feedback();
        /*
         * Если пришли post-данные, загружаем их в модель...
         */
        if ($model->load(Yii::$app->request->post())) {
            // ...и проверяем эти данные
            if ( ! $model->validate()) {
                /*
                 * Данные не прошли валидацию
                 */
                Yii::$app->session->setFlash(
                    'feedback-success',
                    false
                );
                // сохраняем в сессии введенные пользователем данные
                Yii::$app->session->setFlash(
                    'feedback-data',
                    [
                        'name' => $model->name,
                        'email' => $model->email,
                        'body' => $model->body
                    ]
                );
                /*
                 * Сохраняем в сессии массив сообщений об ошибках. Массив имеет вид
                 * [
                 *     'name' => [
                 *         'Поле «Ваше имя» обязательно для заполнения',
                 *     ],
                 *     'email' => [
                 *         'Поле «Ваш email» обязательно для заполнения',
                 *         'Поле «Ваш email» должно быть адресом почты'
                 *     ]
                 * ]
                 */
                Yii::$app->session->setFlash(
                    'feedback-errors',
                    $model->getErrors()
                );
            } else {
                /*
                 * Данные прошли валидацию
                 */

                // отправляем письмо на почту администратора
                $textBody = 'Имя: ' . strip_tags($model->name) . PHP_EOL;
                $textBody .= 'Почта: ' . strip_tags($model->email) . PHP_EOL . PHP_EOL;
                $textBody .= 'Сообщение: ' . PHP_EOL . strip_tags($model->body);

                $htmlBody = '<p><b>Имя</b>: ' . strip_tags($model->name) . '</p>';
                $htmlBody .= '<p><b>Почта</b>: ' . strip_tags($model->email) . '</p>';
                $htmlBody .= '<p><b>Сообщение</b>:</p>';
                $htmlBody .= '<p>' . nl2br(strip_tags($model->body)) . '</p>';

                Yii::$app->mailer->compose()
                    ->setFrom(Yii::$app->params['senderEmail'])
                    ->setTo(Yii::$app->params['adminEmail'])
                    ->setSubject('Заполнена форма обратной связи')
                    ->setTextBody($textBody)
                    ->setHtmlBody($htmlBody)
                    ->send();

                // данные прошли валидацию, отмечаем этот факт
                Yii::$app->session->setFlash(
                    'feedback-success',
                    true
                );
            }
            // выполняем редирект, чтобы избежать повторной отправки формы
            return $this->refresh();
        }
        return $this->render('feedback', ['model' => $model]);
    }


    /*...*/

}
<?php
namespace app\models;

use yii\base\Model;

class Feedback extends Model {

    public $name;
    public $email;
    public $body;

    public function attributeLabels() {
        return [
            'name' => 'Ваше имя',
            'email' => 'Ваш e-mail',
            'body' => 'Ваше сообщение',
        ];
    }

    public function rules() {
        return [
            // удалить пробелы для всех трех полей формы
            [['name', 'email', 'body'], 'trim'],
            // поле name обязательно для заполнения
            ['name', 'required', 'message' => 'Поле «Ваше имя» обязательно для заполнения'],
            // поле email обязательно для заполнения
            ['email', 'required', 'message' => 'Поле «Ваш email» обязательно для заполнения'],
            // поле email должно быть корректным адресом почты
            ['email', 'email', 'message' => 'Поле «Ваш email» должно быть адресом почты'],
            // поле body обязательно для заполнения
            ['body', 'required', 'message' => 'Поле «Сообщение» обязательно для заполнения'],
            // поля name и email должны быть не более 50 символов
            [
                ['name', 'email'],
                'string',
                'max' => 50,
                'tooLong' => 'Поле должно быть длиной не более 50 символов'
            ],
            // поле body должно быть не более 1000 символов
            [
                'body',
                'string',
                'max' => 1000,
                'tooLong' => 'Сообщение должно быть длиной не более 1000 символов'
            ],
        ];
    }
}
<?php
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;

$this->title = 'Обратная связь';

/*
 * Если данные формы не прошли валидацию, получаем из сессии сохраненные
 * данные, чтобы заполнить ими поля формы, не заставляя пользователя
 * заполнять форму повторно
 */
$name = '';
$email = '';
$body = '';
if (Yii::$app->session->hasFlash('feedback-data')) {
    $data = Yii::$app->session->getFlash('feedback-data');
    $name = Html::encode($data['name']);
    $email = Html::encode($data['email']);
    $body = Html::encode($data['body']);
}
?>

<div class="container">
    <?php
    $success = false;
    if (Yii::$app->session->hasFlash('feedback-success')) {
        $success = Yii::$app->session->getFlash('feedback-success');
    }
    ?>
    <div id="response">
        <?php if (!$success): ?>
            <?php if (Yii::$app->session->hasFlash('feedback-errors')): ?>
                <div class="alert alert-warning alert-dismissible" role="alert">
                    <button type="button" class="close"
                            data-dismiss="alert" aria-label="Закрыть">
                        <span aria-hidden="true">&times;</span>
                    </button>
                    <p>При заполнении формы допущены ошибки</p>
                    <?php $allErrors = Yii::$app->session->getFlash('feedback-errors'); ?>
                    <ul>
                        <?php foreach ($allErrors as $errors): ?>
                            <?php foreach ($errors as $error): ?>
                                <li><?= $error; ?></li>
                            <?php endforeach; ?>
                        <?php endforeach; ?>
                    </ul>
                </div>
            <?php endif; ?>
        <?php else: ?>
            <div class="alert alert-success alert-dismissible" role="alert">
                <button type="button" class="close"
                        data-dismiss="alert" aria-label="Закрыть">
                    <span aria-hidden="true">&times;</span>
                </button>
                <p>Ваше сообщение успешно отправлено</p>
            </div>
        <?php endif; ?>
    </div>

    <?php $form = ActiveForm::begin(['id' => 'feedback', 'class' => 'form-horizontal']); ?>
        <?= $form->field($model, 'name')->textInput(['value' => $name]); ?>
        <?= $form->field($model, 'email')->input('email', ['value' => $email]); ?>
        <?= $form->field($model, 'body')->textarea(['rows' => 5, 'value' => $body]); ?>
        <div class="form-group">
            <?= Html::submitButton('Отправить', ['class' => 'btn btn-primary']) ?>
        </div>
    <?php ActiveForm::end(); ?>
</div>

Адрес почты отправителя и получателя мы берем из настроек приложения config/params.php:

<?php

return [
    'adminEmail' => 'admin@example.com',
    'senderEmail' => 'noreply@example.com',
    'senderName' => 'Example.com mailer',
];

Файл письма в директории runtime/mail:

Message-ID: <3bcd3bb26e670ca057eaafb376d365d9@www.example.com>
Date: Wed, 14 Aug 2019 11:57:03 +0300
Subject: =?utf-8?Q?=D0=97=D0=B0=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD?=
 =?utf-8?Q?=D0=B0_=D1=84=D0=BE=D1=80=D0=BC=D0=B0_=D0=BE=D0=B1=D1=80=D0=B0?=
 =?utf-8?Q?=D1=82=D0=BD=D0=BE=D0=B9_=D1=81=D0=B2=D1=8F=D0=B7=D0=B8?=
From: noreply@example.com
To: admin@example.com
MIME-Version: 1.0
Content-Type: multipart/alternative;
 boundary="_=_swift_1565773023_5daac4bcd4b10c645568b6655a1eb78a_=_"


--_=_swift_1565773023_5daac4bcd4b10c645568b6655a1eb78a_=_
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

=D0=98=D0=BC=D1=8F: =D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9
=D0=9F=D0=BE=D1=87=D1=82=D0=B0: ivanov@mail.ru

=D0=A1=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B5:=20
=D0=9A=D0=B0=D0=BA=D0=BE=D0=B5-=D1=82=D0=BE =D1=81=D0=BE=D0=BE=D0=B1=D1=
=89=D0=B5=D0=BD=D0=B8=D0=B5

--_=_swift_1565773023_5daac4bcd4b10c645568b6655a1eb78a_=_
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

<p><b>=D0=98=D0=BC=D1=8F</b>: =D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9</p=
><p><b>=D0=9F=D0=BE=D1=87=D1=82=D0=B0</b>: ivanov@mail.ru</p><p><b>=
=D0=A1=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B5</b>:</p><p>=D0=9A=
=D0=B0=D0=BA=D0=BE=D0=B5-=D1=82=D0=BE =D1=81=D0=BE=D0=BE=D0=B1=D1=89=D0=
=B5=D0=BD=D0=B8=D0=B5</p>

--_=_swift_1565773023_5daac4bcd4b10c645568b6655a1eb78a_=_--

Шаблоны для писем

Способ отправки, который мы рассмотрели выше, подходит для небольших писем. Но если планируется отправить большое письмо, удобнее использовать view-шаблоны, размещенные в директории mail. Давайте создадим в этой директории файл feedback.php:

<?php
use yii\helpers\Html;
?>
<p><strong>Имя</strong>: <?= Html::encode($name); ?></p>
<p><strong>Email</strong>: <?= Html::encode($email); ?></p>
<p><strong>Сообщение</strong>:</p>
<p><?= nl2br(Html::encode($body)); ?></p>

А отправлять почту мы теперь будем так:

$mail = Yii::$app->mailer->compose(
    'feedback',
    [
        'name' => strip_tags($model->name),
        'email' => strip_tags($model->email),
        'body' => strip_tags($model->body)
    ]
);
$mail->setFrom(Yii::$app->params['senderEmail'])
     ->setTo(Yii::$app->params['adminEmail'])
     ->setSubject('Заполнена форма обратной связи')
     ->send();

Наш view-шаблон можно обернуть layout-шаблоном, который расположен в файле mail/layouts/html.php:

<?php
use yii\helpers\Html;

/* @var $this \yii\web\View view component instance */
/* @var $message \yii\mail\MessageInterface the message being composed */
/* @var $content string main view render result */
?>
<?php $this->beginPage() ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=<?= Yii::$app->charset ?>" />
    <title><?= Html::encode($this->title) ?></title>
    <?php $this->head() ?>
</head>
<body>
    <?php $this->beginBody() ?>
    <?= $content ?>
    <?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage() ?>

Для этого указываем в конфигурации путь к layout-шаблону:

$config = [
    /*...*/,
    'components' => [
        /*...*/
        'mailer' => [
            'class' => 'yii\swiftmailer\Mailer',
            'htmlLayout' => 'layouts/html',
            'useFileTransport' => true,
        ],
        /*...*/
    ],
    /*...*/
];

И в view-шаблоне задаем title:

<?php
use yii\helpers\Html;
$this->title = 'Заполнена форма обратной связи';
?>
<p><strong>Имя</strong>: <?= Html::encode($name); ?></p>
<p><strong>Email</strong>: <?= Html::encode($email); ?></p>
<p><strong>Сообщение</strong>:</p>
<p><?= nl2br(Html::encode($body)); ?></p>

В итоге получим такое письмо:

Message-ID: <28000ba51fa0db3459bc8e51f8fab757@www.example.com>
Date: Thu, 15 Aug 2019 09:11:49 +0300
Subject: =?utf-8?Q?=D0=97=D0=B0=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD?=
 =?utf-8?Q?=D0=B0_=D1=84=D0=BE=D1=80=D0=BC=D0=B0_=D0=BE=D0=B1=D1=80=D0=B0?=
 =?utf-8?Q?=D1=82=D0=BD=D0=BE=D0=B9_=D1=81=D0=B2=D1=8F=D0=B7=D0=B8?=
From: noreply@example.com
To: admin@example.com
MIME-Version: 1.0
Content-Type: multipart/alternative;
 boundary="_=_swift_1565849509_f5996ab5ab87f6be9c0a48cd7b75f89f_=_"


--_=_swift_1565849509_f5996ab5ab87f6be9c0a48cd7b75f89f_=_
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

=D0=98=D0=BC=D1=8F: =D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9
Email: ivanov@mail.ru
=D0=A1=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B5:
=D0=9A=D0=B0=D0=BA=D0=BE=D0=B5-=D1=82=D0=BE =D1=81=D0=BE=D0=BE=D0=B1=D1=
=89=D0=B5=D0=BD=D0=B8=D0=B5

--_=_swift_1565849509_f5996ab5ab87f6be9c0a48cd7b75f89f_=_
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org=
/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns=3D"http://www.w3.org/1999/=
xhtml">
<head>
    <meta http-equiv=3D"Content-Type" content=3D"text/ht=
ml; charset=3DUTF-8" />
    <title>=D0=97=D0=B0=D0=BF=D0=BE=D0=BB=D0=
=BD=D0=B5=D0=BD=D0=B0 =D1=84=D0=BE=D1=80=D0=BC=D0=B0 =D0=BE=D0=B1=D1=80=
=D0=B0=D1=82=D0=BD=D0=BE=D0=B9 =D1=81=D0=B2=D1=8F=D0=B7=D0=B8</title>
   =
 </head>
<body>
        <p><strong>=D0=98=D0=BC=D1=8F</strong>: =D0=
=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9</p>
<p><strong>Email</strong>: ivanov@mail.ru</p>
<p><strong>=D0=A1=D0=BE=D0=BE=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B5</strong>:=
</p>
<p>=D0=9A=D0=B0=D0=BA=D0=BE=D0=B5-=D1=82=D0=BE =D1=81=D0=BE=D0=BE=D0=B1=
=D1=89=D0=B5=D0=BD=D0=B8=D0=B5</p>
    </body>
</html>

--_=_swift_1565849509_f5996ab5ab87f6be9c0a48cd7b75f89f_=_--

Если не устраивает layout-шаблон, который предлагается по умолчанию, можно создать свой и указать это в настройках:

$config = [
    /*...*/,
    'components' => [
        /*...*/
        'mailer' => [
            'class' => 'yii\swiftmailer\Mailer',
            'viewPath' => '@app/mail',
            'htmlLayout' => 'layouts/main-html',
            'textLayout' => 'layouts/main-text',
            'messageConfig' => [
                'charset' => 'UTF-8',
                'from' => ['noreply@site.com' => 'Site Name'],
            ],
            'useFileTransport' => true,
        ],
        /*...*/
    ],
    /*...*/
];

В этой конфигурации:

  • viewPath — путь к шаблонам писем
  • htmlLayout и textLayout — html и text макеты писем
  • charset — кодировка писем по умолчанию
  • from — адрес почты и имя отправителя по умолчанию

Реальная отправка почты

Вроде все прошло успешно, можно отправлять письма по-настоящему. Редактируем файл config/web.php:

$config = [
    /*...*/,
    'components' => [
        /*...*/
        'mailer' => [
            'class' => 'yii\swiftmailer\Mailer',
            'htmlLayout' => 'layouts/html',
            'useFileTransport' => false,
            'transport' => [
                'class' => 'Swift_SmtpTransport',
                'host' => 'smtp.mail.ru',
                'username' => 'имя_пользователя@mail.ru',
                'password' => 'пароль_от_почты',
                'port' => '465',
                'encryption' => 'ssl',
            ],
        ],
        /*...*/
    ],
    /*...*/
];

И вносим изменения в файл config/params.php:

return [
    'adminEmail' => 'реальный_адрес_получателя@mail.ru',
    // должен совпадать с имя_пользователя@mail.ru
    'senderEmail' => 'реальный_адрес_отправителя@mail.ru',
    'senderName' => 'Example.com mailer',
];
При отправке писем через SMTP-сервер MAIL.RU содержимое поля From: должно совпадать с именем почтового ящика, в котором была осуществлена SMTP-авторизация: если в настройках почтовой программы указан почтовый ящик ivanov@mail.ru, то именно это имя почтового ящика должно указываться в поле From:.

Дополнительно

Поиск: Web-разработка • Yii2 • Форма • Фреймворк • Почта • Swiftmailer • Обратная связь • Feedback

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.