Email notifications are a crucial part of most websites, especially for eCommerce shops. Magento has a lot of different email notifications that deliver an important notification to a customer and store owner as well.

I would like to go through all steps and see how Magento sends different emails from the technical point of view.

If you need to disable some email notifications in Magento 2, you can check our extension.

Note: all code snippets were taken from the latest Magento Open Source 2.2.3 (community edition)

 

New customer account notification

Let's explore one of the most popular email notifications - new customer account confirmation. Magento sends this type of notification when a user submits a registration form. We assume here that customer confirmation feature is disabled by default.

This type of email notifications is being sent immediately, without queueing.

Everything starts in the controller, account management model receives customer object with extracted data and password.

vendor/magento/module-customer/Controller/Account/CreatePost.php


$customer = $this->accountManagement
                ->createAccount($customer, $password, $redirectUrl);



Account management (\Magento\Customer\Model\AccountManagement) calls createAccountWithPasswordHash method inside createAccount

Then a customer object is being saved using customer repository:


$customer = $this->customerRepository->save($customer, $hash);



And after successful save it sends email notification:


$this->sendEmailConfirmation($customer, $redirectUrl);
// ...
$this->getEmailNotification()->newAccount($customer, $templateType, $redirectUrl, $customer->getStoreId());

Note: getEmailNotification() method is deprecated now.

Here is how \Magento\Customer\Model\EmailNotification::newAccount() looks like:


public function newAccount(
    CustomerInterface $customer,
    $type = self::NEW_ACCOUNT_EMAIL_REGISTERED,
    $backUrl = '',
    $storeId = 0,
    $sendemailStoreId = null
) {
    $types = self::TEMPLATE_TYPES;
    if (!isset($types[$type])) {
        throw new LocalizedException(__('Please correct the transactional account email type.'));
    }
    if (!$storeId) {
        $storeId = $this->getWebsiteStoreId($customer, $sendemailStoreId);
    }
    $store = $this->storeManager->getStore($customer->getStoreId());
    $customerEmailData = $this->getFullCustomerObject($customer);
    $this->sendEmailTemplate(
        $customer,
        $types[$type],
        self::XML_PATH_REGISTER_EMAIL_IDENTITY,
        ['customer' => $customerEmailData, 'back_url' => $backUrl, 'store' => $store],
        $storeId
    );
}



Also, we have DotMailer plugin here \Dotdigitalgroup\Email\Plugin\CustomerEmailNotificationPlugin::aroundNewAccount() which checks if email notification should be disabled depending on the configuration. But by default, DotMailer is disabled, so we are moving forward.

The next method is \Magento\Customer\Model\EmailNotification::sendEmailTemplate()


private function sendEmailTemplate(
    $customer,
    $template,
    $sender,
    $templateParams = [],
    $storeId = null,
    $email = null
) {
    $templateId = $this->scopeConfig->getValue($template, 'store', $storeId);
    if ($email === null) {
        $email = $customer->getEmail();
    }
    $transport = $this->transportBuilder->setTemplateIdentifier($templateId)
        ->setTemplateOptions(['area' => 'frontend', 'store' => $storeId])
        ->setTemplateVars($templateParams)
        ->setFrom($this->scopeConfig->getValue($sender, 'store', $storeId))
        ->addTo($email, $this->customerViewHelper->getCustomerName($customer))
        ->getTransport();
    $transport->sendMessage();
}



This method contains code that is used for most email notifications. Here we create a transport object which sends an email. Let’s have a deeper look at how it works.

The main object for sending emails is \Magento\Framework\Mail\Template\TransportBuilder TransportBuilder requires the following data:

  • Template identifier (template name or template numeric ID if a template is customized in admin)
  • Template Options: area (“frontend” or “adminhtml”) and store ID
  • Template variables (any data that will be used in email template)
  • Sender information (email identity from the configuration, for customer notifications it is “general”)
  • Receiver information (email and name)



By default, TransportBuilder returns \Magento\Email\Model\Transport object instance. If you want, you can replace this transport object with your custom class using dependency injection (argument replacement) or simple class preference.

There are some plugins for sendMessage method:

  • \Magento\Email\Model\Plugin\WindowsSmtpConfig::beforeSendMessage() - sets SMTP port if Magento is running on Windows
  • \Magento\Email\Model\Mail\TransportInterfacePlugin::aroundSendMessage() - checks if email communications is enabled
  • \Dotdigitalgroup\Email\Plugin\TransportPlugin::aroundSendMessage()



After executing all these plugins, finally, Zend Mail component takes control over sending an email. This is the lowest layer and here it calls for PHP mail function:

  • \Zend_Mail_Transport_Abstract::send()
  • \Zend_Mail_Transport_Sendmail::_sendMail()



That's it. In the next article we will review another email notification.