Email is a common communication method between vendors and customers. Email marketing helps you connect with your customers with the aims of brand promotion and sales increasing.

 

Aside from the default email templates in Magento 2, many extensions require their own emails with various custom templates, parameters, and conditions.

 

In this blog post, we will learn about sending email in Magento 2, with code snippets taken from our Magento 2 Abandoned Cart Email extension, in which sending email is the main function to remind customers of their pending carts.

 

1. Declare custom email template

 

To declare a custom email template that goes with your extension, create the file /etc/email_templates.xml:

 

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Email:etc/email_templates.xsd">
    <template id="abandonedcart_item1"
              label="Abandoned Cart Item 1"
              file="abandonedcart_item1.html"
              type="html"
              module="Magenest_AbandonedCart"
              area="frontend"
    />
</config>

 

Here we have declared a template with id abandonedcart_item1, defined by the file abandonedcart_item1.html, belonged to AbandonedCart module and frontend area. This will create an entry named “Abandoned Cart Item 1” belonged to group “Abandoned Cart” (name of host extension) when admins open Marketing -> Email templates. To declare more templates, you can create more template items inside config tags.

 

2. Define the email template

 

As our custom template has frontend area and abandonedcart_item1.html file name, the path to this template is /view/frontend/email/abandonedcart_item1.html.

 

<!--@subject Did you forget something? @-->
{Error in template processing}
<table style="float:left; margin-right: 10px" class="fue-abc-items">
   <tr>
       <td>
           <div class="greeting">Hi {{var customerName}},</div>
           <div>
               I’m from . ,
               you haven’t forgotten us as well as the shopping cart you left in our store recently. 
               Thus, we would love to be sure that below items will catch your attention again.
           </div>
       </td>
   </tr>
   <tr>
       <td>
           {{var cart_items}}
       </td>
   </tr>
   <tr>
       <td style="text-align: center;">
           <div style="margin-top: 30px;">
               Use coupon code 
               <span style="font-weight: bold">{{var coupon.code}}</span>
               at checkout!
           </div>
           <div>{{var checkOpened}}</div>
       </td>
   </tr>
   <tr>
       <td style="text-align: center;">
           If you want to complete your purchase​ just click the link below.
       </td>
   </tr>
   <tr>
       <td style="text-align: center;">
           <a style="color: #fff;border: 1px solid transparent; padding: 16px 32px; outline: none; text-decoration: none; cursor: pointer; text-align: center;border-radius: 4px;"
              href="{{var resumeLink}}" target="_blank">
               <button style="color: white; margin-top: 10px;margin-bottom: 30px;border: 0; -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; background: #000; padding: 10px 15px; -webkit-border-radius:5px; -moz-border-radius:5px; border-radius:5px;">
                   Checkout Now
               </button>
           </a>
       </td>
   </tr>
   <tr>
       <td>
           <div>
               Or if anything got you confused in shopping process?
               Feel free to contact again to discuss problems you may have.
               Your satisfaction is the best product we desire to deliver.
           </div>
       </td>
   </tr>
   <tr>
       <td>
           <div>
               Looking forward to seeing you around!
           </div>
       </td>
   </tr>
   <tr>
       <td>
           <div style="font-size: 9px;text-align: center;">No longer want to receive these email? You can
               <a href="{{var unsubscribeLink}}" style="text-decoration-line: underline;font-weight: 400;">unsubscribe
                   here</a>
           </div>
       </td>
   </tr>
</table>
{Error in template processing}

 

The email template mostly follows html standard with styling. Magento-specific parameters are placed inside double curly braces {{ }} :

 

- trans for texts that support translation (if provided by the extension’s i18n). You can use default variables i.e. $store.getFrontendName() to get the store’s name on the email.

 

- var for variables supplied by the email controller.

 

- template to automatically inject other templates (i.e. header/footer) based on config_path

 

Note: Some email clients support only CSS style as inline on the style attribute of HTML tags.

 

3. Create a transport builder

 

To customize the email function of Magento we will need to create a transport builder class, extending the default Magento Transport Builder. In our case, we will add the function to send pdf and images attachments to the email, using our transport builder located at Magenest/AbandonedCart/Model/Mail/TransportBuilder.php

 

class TransportBuilder extends \Magento\Framework\Mail\Template\TransportBuilder
{
    public function getMessage()
	{
    	return $this->message;
	}

    //create attachment items for email based on parameters
    public function createAttachment($params, $transport = false)
	{
    	$type = isset($params['cat']) ? $params['cat'] : \Zend_Mime::TYPE_OCTETSTREAM;
    	if ($transport === false) {
        	if ($type == 'pdf') {
            	$this->message->createAttachment(
                	$params['body'],
                	'application/pdf',
                	\Zend_Mime::DISPOSITION_ATTACHMENT,
                	\Zend_Mime::ENCODING_BASE64,
                	$params['name']
            	);
        	} elseif ($type == 'png') {
            	$this->message->createAttachment(
                	$params['body'],
                	'image/png',
                	\Zend_Mime::DISPOSITION_ATTACHMENT,
                	\Zend_Mime::ENCODING_BASE64,
                	$params['name']
            	);
        	} else {
$encoding = isset($params['encoding']) ? $params['encoding'] : \Zend_Mime::ENCODING_BASE64;
            	$this->message->createAttachment(
                	$params['body'],
                	$type,
                	\Zend_Mime::DISPOSITION_ATTACHMENT,
                	$encoding,
                	$params['name']
            	);
        	}
    	} else {
        	$this->addAttachment($params, $transport);
    	}
    	return $this;
	}

	public function addAttachment($params, $transport)
	{
    	$zendPart = $this->createZendMimePart($params);
    	$parts	= $transport->getMessage()->getBody()->addPart($zendPart);
    	$transport->getMessage()->setBody($parts);
	}

	protected function createZendMimePart($params)
	{
    	if (class_exists('Zend\Mime\Mime') && class_exists('Zend\Mime\Part')) {
        	$type          	= isset($params['type']) ? $params['type'] : \Zend\Mime\Mime::TYPE_OCTETSTREAM;
        	$part          	= new \Zend\Mime\Part(@$params['body']);
        	$part->type    	= $type;
        	$part->filename	= @$params['name'];
        	$part->disposition = \Zend\Mime\Mime::DISPOSITION_ATTACHMENT;
        	$part->encoding	= \Zend\Mime\Mime::ENCODING_BASE64;
        	return $part;
    	} else {
        		throw new \Exception("Missing Zend Framework Source");
    	}
	}

	public function reset()
	{
    		return parent::reset();
	}
}

 

Here we have created not one, but two functions for making attachments. This is due to changes in Magento 2.3’s transport builder class compared to Magento 2.1 and 2.2: from 2.3.0 onward, Magento 2 employs Zend Framework 2 for sending emails, with significant changes to email attachments compared to Zend Framework 1.

 

In our extension, we determine if Zend Framework 1 or 2 should be used for making attachments based on $transport variable passed to createAttachment() function.

 

4. Send the email

 

To send emails from a class, its constructor needs to call a transportbuilder class. You can use the default one Magento\Framework\Mail\Template\TransportBuilder or a custom one (Magenest\AbandonedCart\Model\Mail\TransportBuilder in our case).

 

Below is a snippet of sendMail() function which covers all steps to create an email object:

 

use Magenest\AbandonedCart\Model\Mail\TransportBuilder
...
public function sendMail()
{
	//define variables for the email generate email template with template file and templates variables 
	$template = 'abandonedcart_item1';
	$recipient_address = '[recipient email]';
	$recipient_name = ‘[recipient name]’;
	$from_address = ‘[
  		‘Name’ => [sender name],
  ‘Email’ => [sender email]
]’;
	//an array with variables, format is key = variable name, value = variable value
	$vars = [
   	    ...
	];
	//several variables in email template, i.e. storeName are generated based on store Id
	$storeId = [store Id];

	$this->_inlineTranslation->suspend();
	$version = $this->_helperData->getVersionMagento();
	if(version_compare($version,'2.2.0') < 0){
    	//create email object for Magento 2.1.x
    	$this->_transportBuilder->setTemplateIdentifier(
        	$template
    	)->setTemplateOptions(
        	[
            	'area'  => \Magento\Framework\App\Area::AREA_FRONTEND,
            	'store' => $storeId,
        	]
    	)->setTemplateVars(
        	$vars
    	)->addTo(
        	$recipient_address,
        	$recipient_name
    	);
	}
	//create email template for Magento 2.2.x and 2.3.x
else {
    	$this->_transportBuilder->setTemplateIdentifier(
        	$template
    	)->setTemplateOptions(
        	[
            	'area'  => \Magento\Framework\App\Area::AREA_FRONTEND,
            	'store' => $storeId,
        	]
    	)->setTemplateVars(
        	$vars
    	)->setFrom(
        	$from_address
    	)->addTo(
        	$recipient_address,
        	$recipient_name
    	);
	}

	//add bcc email address
	$bccMail = ‘[email address]’;
   	$this->_transportBuilder->addBcc($bccMail);

	//add attachments to email object
	if ($attachments) {
//check version of Zend Framework in use based on the existence of createAttachment function in Magento 2’s default transportBuiler
    	if (method_exists($this->_transportBuilder->getMessage(), 'createAttachment')) {
        	foreach ($attachments as $attachment) {
            	if ($attachment) {
                	$this->_transportBuilder->createAttachment($attachment);
            	}
        	}
        	$transport = $this->_transportBuilder->getTransport();
    	} else {
        	$transport = $this->_transportBuilder->getTransport();
        	foreach ($attachments as $attachment) {
            	if ($attachment) {
                	$this->_transportBuilder->createAttachment($attachment, $transport);
            	}
        	}
    	}
	}

	//create the transport
	if (!isset($transport)) {
    	$transport = $this->_transportBuilder->getTransport();
	}
	
	//send the email
	try {
    	$transport->sendMessage();
    	$this->_inlineTranslation->resume();
	} catch (\Exception $exception) {
	//log failed email send operation
    	$this->_logger->critical($exception->getMessage());
	}
}

 

Finally, our email will look as follows

 

send-email-in-magento-2-programmatically
The final email we got

 

With the four steps above, we have covered the basics of sending emails programmatically in Magento 2. Please leave a comment below if you have any questions. Thanks for reading!