How to Seamlessly Add Attachments to Magento Email

In Magento, enhancing your communication strategy can significantly impact customer satisfaction and engagement. One effective way to achieve this is to add attachments to your emails, providing customers with comprehensive information, and improving the overall user experience. 

In this blog post, we’ll guide you through the process of creating a Magento module to seamlessly attach invoices to your emails, enhancing the overall user experience.

Why adding attachments to Magento email is a valuable enhancement

Attaching files to Magento email proves beneficial for both enterprises and customers alike.

With businesses

Efficiency and Time Savings

With the automatic preparation of invoice attachments beforehand, businesses save time and effort. Senders can effortlessly click “send”, streamlining the communication process.

Professional Brand Image

Including well-designed attachments contributes to a professional image for the company. It reflects a commitment to organized and detailed communication, enhancing the overall perception of the business.

With customers

Comprehensive Order Details

Attachments provide customers with detailed order information, such as invoices, receipts, and shipping details, directly within the email. This eliminates the need for customers to navigate external platforms and ensures a comprehensive understanding of their purchase.

Simplified Communication

Attachments offer an easy-to-follow format, reducing the need for customers to read lengthy emails. Important information is presented clearly and concisely, enhancing the overall user experience.

Step-by-step Instructions to Add an Invoice Attachment to Magento Email

1. Create Module Files

To begin, you need to set up the basic structure of your module with the following files:

File di.xml

<?xml version="1.0"?>
 
 
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
   <preference for="Magento\Framework\Mail\Template\TransportBuilder"
           	type="Tigren\SendPdf\Mail\Template\TransportBuilder"/>
   <type name="Magento\Sales\Model\Order\Email\SenderBuilder">
   	<plugin name="add.attachment.email" type="Tigren\SendPdf\Plugin\SenderBuilder"/>
   </type>
</config>

File module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
   <module name="Tigren_SendPdf" />
</config>

File registration.php

<?php
/*
* @author  Tigren Solutions <[email protected]>
* @copyright Copyright (c) 2023 Tigren Solutions <https://www.tigren.com>. All rights reserved.
* @license  Open Software License (“OSL”) v. 3.0
*/
 
use Magento\Framework\Component\ComponentRegistrar;
 
ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Tigren_SendPdf', __DIR__);

2. Override TransportBuilder

Then, we need to override the TransportBuilder file in the Tigren\SendPdf\Mail\Template directory of Magento to include the attachment functionality.

<?php
 
declare (strict_types=1);
 
namespace Tigren\SendPdf\Mail\Template;
 
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Mail\AddressConverter;
use Magento\Framework\Mail\EmailMessageInterfaceFactory;
use Magento\Framework\Mail\MessageInterface;
use Magento\Framework\Mail\MessageInterfaceFactory;
use Magento\Framework\Mail\MimeMessageInterfaceFactory;
use Magento\Framework\Mail\MimePartInterfaceFactory;
use Magento\Framework\Mail\Template\FactoryInterface;
use Magento\Framework\Mail\Template\SenderResolverInterface;
use Magento\Framework\Mail\TransportInterfaceFactory;
use Magento\Framework\ObjectManagerInterface;
use Laminas\Mime\Mime;
use Laminas\Mime\Message;
use Laminas\Mime\PartFactory;
 
/**
* Class TransportBuilder
* @package Tigren\SendPdf\Mail\Template
*/
class TransportBuilder extends \Magento\Framework\Mail\Template\TransportBuilder
{
   /**
	* @var Message
	*/
   protected $messageMime;
 
   /**
	* @var
	*/
   protected $message;
 
   /**
	* @var array
	*/
   protected $attachments = [];
 
   /**
	* @var PartFactory|mixed
	*/
   protected $partFactory;
 
   /**
	* @param Message $messageMime
	* @param PartFactory $partFactory
	* @param FactoryInterface $templateFactory
	* @param MessageInterface $message
	* @param SenderResolverInterface $senderResolver
	* @param ObjectManagerInterface $objectManager
	* @param TransportInterfaceFactory $mailTransportFactory
	* @param MessageInterfaceFactory|null $messageFactory
	* @param EmailMessageInterfaceFactory|null $emailMessageInterfaceFactory
	* @param MimeMessageInterfaceFactory|null $mimeMessageInterfaceFactory
	* @param MimePartInterfaceFactory|null $mimePartInterfaceFactory
	* @param AddressConverter|null $addressConverter
	*/
   public function __construct(
   	Message $messageMime,
   	PartFactory $partFactory,
   	FactoryInterface $templateFactory,
   	MessageInterface $message,
   	SenderResolverInterface $senderResolver,
   	ObjectManagerInterface $objectManager,
   	TransportInterfaceFactory $mailTransportFactory,
   	MessageInterfaceFactory $messageFactory = null,
   	EmailMessageInterfaceFactory $emailMessageInterfaceFactory = null,
   	MimeMessageInterfaceFactory $mimeMessageInterfaceFactory = null,
   	MimePartInterfaceFactory $mimePartInterfaceFactory = null,
   	AddressConverter $addressConverter = null
   ) {
   	$this->templateFactory = $templateFactory;
   	$this->partFactory = $partFactory;
   	$this->messageMime = $messageMime;
   	parent::__construct(
       	$templateFactory,
       	$message,
       	$senderResolver,
       	$objectManager,
       	$mailTransportFactory,
       	$messageFactory,
       	$emailMessageInterfaceFactory,
       	$mimeMessageInterfaceFactory,
       	$mimePartInterfaceFactory,
       	$addressConverter
   	);
   }
 
   /**
	* @return $this|TransportBuilder
	* @throws LocalizedException
	*/
 protected function prepareMessage()
   {
   	$result = parent::prepareMessage();
   	if (!empty($this->attachments)) {
       	foreach ($this->attachments as $attachment) {
           	$body = $this->message->getBody();
           	if (!$body) {
               	$body = $this->messageMime;
           	}
           	$body->addPart($attachment);
           	$this->message->setBody($body);
       	}
       	$this->attachments = [];
   	}
   	return $result;
   }
 
   /**
	* @param $content
	* @param $fileName
	* @param $fileType
	* @return $this
	*/
   public function addAttachment($content, $fileName, $fileType)
   {
   	$attachmentPart = $this->partFactory->create();
   	$attachmentPart->setContent($content)
       	->setType($fileType)
       	->setFileName($fileName)
       	->setDisposition(Mime::DISPOSITION_ATTACHMENT)
       	->setEncoding(Mime::ENCODING_BASE64);
   	$this->attachments[] = $attachmentPart;
 
   	return $this;
   }
}

3. Plugin for SenderBuilder

<?php
 
namespace Tigren\SendPdf\Plugin;
 
use Laminas\Validator\Date;
use Tigren\SendPdf\Mail\Template\TransportBuilder;
use Magento\Sales\Model\Order\Email\Container\Template;
use Magento\Sales\Model\Order\Pdf\Invoice;
use Magento\Framework\Stdlib\DateTime\DateTime;
 
class SenderBuilder
{
   private TransportBuilder $transportBuilder;
 
   private Template $templateContainer;
 
   private Invoice $renderInvoice;
 
   private DateTime $dateTime;
 
   public function __construct(
   	DateTime $dateTime,
   	Invoice $renderInvoice,
   	Template $templateContainer,
   	TransportBuilder $transportBuilder
   ) {
   	$this->transportBuilder = $transportBuilder;
   	$this->templateContainer = $templateContainer;
   	$this->renderInvoice = $renderInvoice;
   	$this->dateTime = $dateTime;
   }
 
   public function beforeSend(\Magento\Sales\Model\Order\Email\SenderBuilder $subject)
   {
   	$dataInvoice = $this->_getDataTemplate();
   	try {
       	if(!empty($dataInvoice)){
           	$pdfContent = $this->renderInvoice->getPdf($dataInvoice)->render();
           	$date = $this->dateTime->date('Y-m-d_H-i-s');
           	$this->transportBuilder->addAttachment($pdfContent, 'invoice' . $date . '.pdf', 'application/pdf');
       	}
   	} catch (\Exception $e) {
       	return;
   	}
   }
 
   private function _getDataTemplate()
   {
   	$data = $this->templateContainer->getTemplateVars();
   	if (array_key_exists('invoice_id', $data)) {
       	return [$data['invoice']];
   	}
   	if (isset($data['order']) && $data['order']->hasInvoices()) {
       	return $data['order']->getInvoiceCollection()->getItems();
   	}
   	return [$data['invoice']] ?? '';
   }

Result

When an email for an invoice or order is dispatched, an invoice attachment is seamlessly appended to the email.

Conclusion

Following these steps, you can seamlessly add invoice attachments to your Magento emails. This enhances your communication strategy, providing customers with detailed information and contributing to a positive shopping experience. 

Customize the module as needed for your specific use case, and elevate your customer engagement in the competitive world of e-commerce. Reach out to us for more tailor-made services that cater specifically to the needs of your online store.