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]) ?>
 
                <?php 
                    // To regenerate new captcha after each refresh, call getVerifyCode(). 
                    // See: http://www.yiiframework.com/forum/index.php/topic/17638-captcha-code-not-changing
                    $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

CaptchaAction requires either GD2 extension or ImageMagick PHP extension. Check that it meets this requirement.

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 field:

<?php 
   // To regenerate new captcha after each refresh, call getVerifyCode(). 
   // See: http://www.yiiframework.com/forum/index.php/topic/17638-captcha-code-not-changing/.
   $this->context->createAction('captcha')->getVerifyCode(true); 
?>
<?= $form->field($model, 'verifyCode')->widget(Captcha::className(), [
     'captchaAction' => 'site/captcha',  // redirect to correct controller where captcha is defined
     'template'      => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',
]) ?>

See more: