This is an old revision of the document!


Yii 2 Email Tools
Setup Emailer (Swiftmailer)

Setup mailer in app/config/main.php (or main-local.php).

  • Basic application:
    • app/config/web.php
    • app/config/console.php
  • Advanced application:
    • common/config/main.php.
    • NOTE: you can also find settings in:
      • environments/prod/common/config/main-local.php
      • environments/dev/common/config/main-local.php
return [
    'components' => [
        ...
        'mailer' => [
            'class' => 'yii\swiftmailer\Mailer',
 
            // Directory that contains the view files 
            // for composing mail messages. Defaults to '@app/mail'
            'viewPath' => '@common/mail',
 
            // Send all mail messages to a file by default. 
            // The messages get stored locally under '@app/runtime/mail'
            'useFileTransport' => true,
            //'fileTransportPath' => '@runtime/mail',
 
            // Send all mail messages as real emails.
            // Set 'useFileTransport' to false,
            // and configure a transport for the mailer.
            // 'useFileTransport' => false,
            //'transport' => [
            //    'class' => 'Swift_SmtpTransport',
            //    'host' => 'localhost',
            //    'username' => 'username',
            //    'password' => 'password',
            //    'port' => '587',
            //    'encryption' => 'tls',
            //],
        ],
    ],
];

To send an email directly from a controller (no form/view):

public function sendMessage($srcMail, $srcName, $dstEmail, $subject, $textBody)
{
    $success = Yii::$app->mailer->compose()
            ->setFrom([$srcMail => $srcName])
            ->setTo($dstEmail)
            ->setSubject($subject)
            ->setTextBody($textBody)
            ->send();
 
    return $success;
}

To send an email with a form (view), you may use the following code in a controller. Eg: controller @app/controllers/SiteController:

class SiteController extends \yii\web\Controller
{
    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        return [
        'access' => [
                'class' => AccessControl::className(),
                'rules' => [
                    //...
                    [
                        // any user (authenticated or not)
                        'actions' => ['contact', 'captcha', ...],
                        'allow' => true,
                    ],
                ],
            ],
        ];
    }
 
    //...
 
    private function sendMessage($srcMail, $srcName, $dstEmail, $subject, $textBody)
    {
       $success = Yii::$app->mailer->compose('contact/html' /* view */, ['contactForm' => $form] /* view params */)
          ->setFrom('from@domain.com')
          ->setTo($form->email)
          ->setSubject($form->subject)
          ->send();
      return $success;
    }

View @app/views/site/contact:

<?php
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use yii\captcha\Captcha;
 
/* @var $this yii\web\View */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model app\models\ContactForm */
 
$this->title = 'Contact';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-contact">
    <h1><?= Html::encode($this->title) ?></h1>
 
    <?php if (Yii::$app->session->hasFlash('contactFormSubmitted')): ?>
 
    <div class="alert alert-success">
        Thank you for contacting us. We will respond to you as soon as possible.
    </div>
 
    <p>
        Note that if you turn on the Yii debugger, you should be able
        to view the mail message on the mail panel of the debugger.
        <?php if (Yii::$app->mailer->useFileTransport): ?>
        Because the application is in development mode, the email is not sent but saved as
        a file under <tt><?= Yii::getAlias(Yii::$app->mailer->fileTransportPath) ?></tt>.
        Please configure the <tt>useFileTransport</tt> property of the <tt>mail</tt>
        application component to be false to enable email sending.
        <?php endif; ?>
    </p>
 
    <?php else: ?>
 
    <p>
        If you have business inquiries or other questions, please fill out the following form to contact us. Thank you.
    </p>
 
    <div class="row">
        <div class="col-lg-5">
            <?php $form = ActiveForm::begin(['id' => 'contact-form']); ?>
                <?= $form->field($model, 'name') ?>
                <?= $form->field($model, 'email') ?>
                <?= $form->field($model, 'subject') ?>
                <?= $form->field($model, 'body')->textArea(['rows' => 6]) ?>
                <!-- // To regenerate new captcha after each refresh, call getVerifyCode(). 
                     // See: http://www.yiiframework.com/forum/index.php/topic/17638-captcha-code-not-changing
                 -->
                <?php $this->context->createAction('captcha')->getVerifyCode(true) ?>
                <?= $form->field($model, 'verifyCode')->widget(Captcha::className(), [
                    'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',
                ]) ?>
                <div class="form-group">
                    <?= Html::submitButton('Submit', ['class' => 'btn btn-primary', 'name' => 'contact-button']) ?>
                </div>
            <?php ActiveForm::end(); ?>
        </div>
    </div>
 
    <?php endif; ?>
</div>

See also:

Email Model

Create model @app/models/Email:

<?php
 
namespace app\models;
 
use Yii;
use yii\base\Model;
 
/**
 * Email is the model behind simple email messages.
 */
class Email extends Model
{
    public $name;
    public $email;
    public $subject;
    public $body;
    //public $verifyCode;
 
    /**
     * @return array the validation rules.
     */
    public function rules()
    {
        return [
            // name, email, subject and body are required
            [['name', 'email', 'subject', 'body'], 'required'],
            // email has to be a valid email address
            ['email', 'email'],
            // verifyCode needs to be entered correctly
            //['verifyCode', 'captcha'],
        ];
    }
 
    /**
     * @return array customized attribute labels
     */
    public function attributeLabels()
    {
        return [
            //'verifyCode' => 'Verification Code',
        ];
    }
 
    /**
     * Sends an email to the specified email address using the information collected by this model.
     * @param  string  $dstEmail - the target email address
     * @return boolean whether the model passes validation
     */
    public function sendMessage($dstEmail)
    {
        if ($this->validate()) {
            Yii::$app->mailer->compose()
                ->setTo($dstEmail)
                ->setFrom([$this->email => $this->name])
                ->setSubject($this->subject)
                ->setTextBody($this->body)
                ->send();
 
            return true;
        } else {
            return false;
        }
    }
}
Console Email

Create a console action to email:

<?php
//----------------------------------------------------------------------------------------
// Controller:     /[console/controllers|command]/EmailNotificationController.php
// Usage:
//   === Execute as a console script ===
//       $ yii email-notification/send-email dstEmail=info@acme.com workOrderId=1
//
//----------------------------------------------------------------------------------------
 
namespace app\commands;
 
use Yii;
use yii\console\Controller;
use app\models\Email;
use app\models\WorkOrder;
use yii\helpers\Console;
 
/**
 * 
 */
class EmailNotificationController extends Controller
{
    /**
     * This command send email message.
     * @param string $message the message to be echoed.
     */
    public function actionSendEmail($dstEmail, $workOrderId)
    {
        defined('STDIN')  or define('STDIN',  fopen('php://stdin',  'r'));
        defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
 
        // strip field name: dstEmail=info@acme.com
        if (stripos($dstEmail, '=') !== false) {
            $params['dstEmail']    = explode('=', $dstEmail); //, -1 /* last item */);
            $dstEmail    = $params['dstEmail'][1];
        }
        // strip field name: workOrderId=1
        if (stripos($workOrderId, '=') !== false) {
            $params['workOrderId'] = explode('=', $workOrderId); //, -1 /* last item */);
            $workOrderId = $params['workOrderId'][1];
        }
 
        $this->stdout("\n");
        $this->stdout("Dest Email: " . $dstEmail    . "\n", Console::BOLD);
        $this->stdout("Work Order: " . $workOrderId . "\n", Console::BOLD);
        $this->stdout("\n");
 
        if (($workOrder = WorkOrder::findOne($workOrderId)) !== null) {
            $workOrderID = str_pad($workOrder->id, 6, "0", STR_PAD_LEFT);
 
            // Setup email data
            $model = new Email();
            $model->name    = Yii::$app->params['companyNameShort'];
            $model->email   = Yii::$app->params['adminEmail'];
            $model->subject = "Work Order [{$workOrderID}]: {$workOrder->task->name}";
            $model->body    = "There is a new work order: \n" .
                              "------------------------------------------------------------------\n" . 
                              "Work Order [{$workOrderID}]: {$workOrder->task->name}\n" .
                              "Date: " . date("M-d-Y", strtotime($workOrder->date)) . "\n" .
                              "Location: {$workOrder->property->name}\n" .
                              "------------------------------------------------------------------\n\n" . 
                              "Details: \n{$workOrder->notes}\n";
            //$model->verifyCode;
 
            $data['name']    = Yii::$app->params['companyNameShort'];
            $data['email']   = Yii::$app->params['adminEmail'];
            $data['subject'] = "Work Order [{$workOrderID}]: {$workOrder->task->name}";
            $data['body']    = "There is a new work order: \n" .
                               "Work Order [{$workOrderID}]: {$workOrder->task->name}\n" .
                               "Date: " . date("M-d-Y", strtotime($workOrder->date)) . "\n" .
                               "Location: {$workOrder->property->name}\n" .
                               "Details: \n{$workOrder->notes}\n";
            //$data['verifyCode'] = '';
 
            // Send email
            //if ($model->load($data) && $model->sendMessage($dstEmail)) {
            //if ($model->load($data)) {
                if ($model->sendMessage($dstEmail)) {
                    $this->stdout("--------------------------------------\n", Console::BOLD);
                    $this->stdout("Email Notification Sent\n", Console::BOLD);
                    $this->stdout("--------------------------------------\n", Console::BOLD);
                    $this->stdout("Subject: " . $data['subject'] . "\n", Console::BOLD);
                    $this->stdout("Message:\n" . $data['body'] . "\n", Console::BOLD);
                    $this->stdout("--------------------------------------\n", Console::BOLD);
                    $this->stdout("Mailer: ".print_r(Yii::$app->mailer, true)."\n", Console::BOLD);
                    $this->stdout("--------------------------------------\n", Console::BOLD);
                    return Controller::EXIT_CODE_NORMAL;
                } else {
                    $this->stdout("Email Notification NOT Sent\n - Unable to post data for: Work Order ID " . 
                        $workOrderId . "\n", Console::BOLD);
                    return Controller::EXIT_CODE_ERROR;
                }
            /*} else {
                //return $this->render('contact', [
                //    'model' => $model,
                //]);
                //return $this->goHome();  // nothing to render
                $this->stdout("Email Notification NOT Sent\nUnable to load data:\n" . $data['subject'] . "\n" . 
                     $data['body'] . "\n", Console::BOLD);
                return Controller::EXIT_CODE_NORMAL;  // nothing to render
            }*/
        } else {
            $this->stdout("Email Notification NOT Sent\n - Work Order ID " . $workOrderId . " not found\n", 
                Console::BOLD);
            return Controller::EXIT_CODE_ERROR;
        }
    }
}

Running Console Action From CLI

Call console action from command line:

$ yii email-notification/send-email dstEmail=info@acme.com workOrderId=1

Running Console Action From View

Call console action from web view: @app/views/WorkOrder/view

...
<?= Html::a(Yii::t('app', 'Notify by Email'), 
      ['work-order/send-email', 'id' => $model->id], ['class' => 'btn btn-primary']
   ) ?>
 
...
Flash Alert Zone
<div class="col-lg-12">
    <?php if(Yii::$app->session->hasFlash('success')): ?>
        <div class="alert alert-success" role="alert">
            <?= Yii::$app->session->getFlash('success'); ?>
        </div>
    <?php endif; ?>
    <?php if(Yii::$app->session->hasFlash('error')): ?>
        <div class="alert alert-danger" role="alert">
            <?= Yii::$app->session->getFlash('error'); ?>
        </div>
    <?php endif; ?>
</div>   

@app/controllers/WorkOrderController

<?php
namespace app\controllers;
 
use Yii;
use app\models\WorkOrder;
use app\commands\EmailNotificationController;
...
 
/**
 * WorkOrderController implements the CRUD actions for WorkOrder model.
 */
class WorkOrderController extends Controller
{
    ...
    public function actionSendEmail($id)
    {
        // use app\commands\EmailNotificationController;
        $command = new EmailNotificationController("", "");  // "" values are a required placeholder
        $command->actionSendEmail(Yii::$app->params['taskAssigneeEmail'], $id);  // $dstEmail, $workOrderId
 
        Yii::$app->session->setFlash('success', "Sent Email");
        return $this->redirect(['view', 'id' => $id]);
    }
}    
Send PDF Attachment Through Email

Assuming you have created a PDF document using TCPDF, you can attach it to an email message as follows:

function sendEmail($model, $pdf, $filename)
{
    // Setup email content
    $emailSubj  = "Report by Email";
 
    $emailBody  = "Hi {$model->customer->contact}, \n\n";
    $emailBody  .= "Please see the included attachment.\n\n";
    $emailBody  .= "Sales Team";
 
    // Attach PDF document as MIME multipart
    $attachment = $pdf->Output("{$filename}", 'S');    // document as a string
 
    // Create and send email message
    Yii::$app->mailer->compose()
        ->setFrom([Yii::$app->params['adminEmail'] => Yii::$app->params['companyName']])
        ->setTo($model->customer->email)
        ->setBcc([
            //Yii::$app->params['adminEmail'] => Yii::$app->params['companyNameShort'] . ' Support', 
            Yii::$app->params['debugEmail'] => 'Debug Email'
        ])
        ->setSubject($emailSubj)
        ->setTextBody($emailBody)
        ->attachContent($attachment, ['fileName' => $filename, 'contentType' => 'application/pdf'])
        ->send();
}
Setup Captcha

In controller's function behaviors(), add captcha to actions for user group that requires the use ot if:

/**
 * Site controller
 */
class SiteController extends Controller
{
    //...
 
    /*
     * @inheritdoc
     */
    public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::className(),
                'rules' => [
                    //...
                    [
                        // any user (authenticated or not)
                        'actions' => ['contact-us', 'captcha', ...],
                        'allow' => true,
                    ],
                ],
            ],
            //...
        ];
    }
 
    //...
}

In controller, add CaptchaAction to actions():

/**
 * Site controller
 */
class SiteController extends Controller
{
    //...
 
    /**
     * @inheritdoc
     */
    public function actions()
    {
        return [
            'error' => [
                'class' => 'yii\web\ErrorAction',
            ],
            'captcha' => [
                'class'           => 'yii\captcha\CaptchaAction',
                'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
                'testLimit'       => 1,
                'foreColor'       => 0xA02040,
            ],
        ];
    }
    //...
}

In model using the captcha, add verifyCode entry to the rules() function:

/**
 * ContactForm is the model behind the contact form.
 */
class ContactForm extends Model
{
    public $name;
    public $email;
    public $subject;
    public $body;
    public $verifyCode;
 
    /**
     * @return array the validation rules.
     */
    public function rules()
    {
        return [
            // name, email, subject and body are required
            [['name', 'email', 'subject', 'body'], 'required'],
            // email has to be a valid email address
            ['email', 'email'],
            // verifyCode needs to be entered correctly
            ['verifyCode', 'captcha', 'captchaAction' => 'site/captcha'],
        ];
    }
 
    //...
}

In form view using the captcha, add verifyCode entry to the rules() function: