The Magento 2.3.x versions use the Zend Framework 2. The implementation of various functionalities is different from the previous versions as it refuses to apply Zend Framework 1 (ZF1).
One such function is to send Emails with attachments. With the function createAttachment() in the previous Magento 2 versions, it was easy to add attachments in Emails.
However, if you are using the upgraded versions, use the below code to add attachments with Email in Magento 2.3.x.
You may refer the below manuals for more knowledge about ZF2 components:
Method to Add Attachments with Email in Magento 2.3.x:
1. Add following code in di.xml
Note:
- If you want to attach the file in frontend – vendor\module\etc\frontend\di.xml
- If you want to attach the file in backend– vendor\module\etc\adminhtml\di.xml
- If you want to attach the file in both frontend and backend – vendor\module\etc\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"> <type name="Vendor\Module\Mail\Template\TransportBuilder"> <arguments> <argument name="message" xsi:type="object" >vendor\module\Mail\Message</argument> <argument name="messageFactory" xsi:type="object" >vendor\module\Mail\MessageFactory</argument> </arguments> </type> </config>
2. Create Message.php file at Vendor\Module\Mail\Message.php
<?php namespace Vendor\Module\Mail; use Zend\Mime\Mime; use Zend\Mime\PartFactory; use Zend\Mail\MessageFactory as MailMessageFactory; use Zend\Mime\MessageFactory as MimeMessageFactory; use Magento\Framework\Mail\MailMessageInterface; class Message implements MailMessageInterface { protected $partFactory; protected $mimeMessageFactory; private $zendMessage; protected $parts = []; public function __construct( PartFactory $partFactory, MimeMessageFactory $mimeMessageFactory, $charset = 'utf-8' ) { $this->partFactory = $partFactory; $this->mimeMessageFactory = $mimeMessageFactory; $this->zendMessage = MailMessageFactory::getInstance(); $this->zendMessage->setEncoding($charset); } public function setBodyText($content) { $textPart = $this->partFactory->create(); $textPart->setContent($content) ->setType(Mime::TYPE_TEXT) ->setCharset($this->zendMessage->getEncoding()); $this->parts[] = $textPart; return $this; } public function setBodyHtml($content) { $htmlPart = $this->partFactory->create(); $htmlPart->setContent($content) ->setType(Mime::TYPE_HTML) ->setCharset($this->zendMessage->getEncoding()); $this->parts[] = $htmlPart; return $this; } public function setBodyAttachment($content, $fileName) { $fileType = Mime::TYPE_OCTETSTREAM; $attachmentPart = $this->partFactory->create(); $attachmentPart->setContent($content) ->setType($fileType) ->setFileName($fileName) ->setDisposition(Mime::DISPOSITION_ATTACHMENT) ->setEncoding(Mime::ENCODING_BASE64); $this->parts[] = $attachmentPart; return $this; } public function setPartsToBody() { $mimeMessage = $this->mimeMessageFactory->create(); $mimeMessage->setParts($this->parts); $this->zendMessage->setBody($mimeMessage); return $this; } public function setBody($body) { return $this; } public function setSubject($subject) { $this->zendMessage->setSubject($subject); return $this; } public function getSubject() { return $this->zendMessage->getSubject(); } public function getBody() { return $this->zendMessage->getBody(); } public function setFrom($fromAddress) { $this->zendMessage->setFrom($fromAddress); return $this; } public function addTo($toAddress) { $this->zendMessage->addTo($toAddress); return $this; } public function addCc($ccAddress) { $this->zendMessage->addCc($ccAddress); return $this; } public function addBcc($bccAddress) { $this->zendMessage->addBcc($bccAddress); return $this; } public function setReplyTo($replyToAddress) { $this->zendMessage->setReplyTo($replyToAddress); return $this; } public function getRawMessage() { return $this->zendMessage->toString(); } public function setMessageType($type) { return $this; } }
3. Create MessageFactory.php file at Vendor\Module\Mail\MessageFactory.php
<?php namespace Vendor\Module\Mail; class MessageFactory extends \Magento\Framework\Mail\MessageInterfaceFactory { /** * @var \Magento\Framework\ObjectManagerInterface */ protected $_objectManager; /** * Factory constructor * * @param \Magento\Framework\ObjectManagerInterface $objectManager * @param string $instanceName */ public function __construct(\Magento\Framework\ObjectManagerInterface $objectManager, $instanceName = '\\Extait\\Attachment\\Mail\\Message') { $this->_objectManager = $objectManager; $this->_instanceName = $instanceName; } /** * Create class instance with specified parameters * * @param array $data * @return \Magento\Framework\Mail\MessageInterface */ public function create(array $data = []) { return $this->_objectManager->create($this->_instanceName, $data); } }
4. Create TransportBuilder.php file at Vendor\Module\Model\Mail\Template\TransportBuilder.php
<?php namespace Vendor\Module\Model\Mail\Template; class TransportBuilder extends \Magento\Framework\Mail\Template\TransportBuilder { public function addAttachment($fileString,$filename) { $arrContextOptions = [ "ssl" => [ "verify_peer" => false, "verify_peer_name" => false, ] ]; /* if $fileString is url of file */ $this->message->setBodyAttachment(file_get_contents($fileString, false, stream_context_create($arrContextOptions)), $filename); /* if $fileString is string data */ $this->message->setBodyAttachment($fileString, $filename); return $this; } protected function prepareMessage() { parent::prepareMessage(); $this->message->setPartsToBody(); return $this; } }
5. Use TransportBuilder object in the file where you want to add an attachment in the Email.
For example Vendor/Module/Helper/Data.php
<?php namespace Vendor\Extension\Helper; use Magento\Customer\Model\Session; class Data extends \Magento\Framework\App\Helper\AbstractHelper { const XML_PATH_EMAIL_DEMO = 'emaildemo/email/email_demo_template'; protected $inlineTranslation; protected $transportBuilder; protected $template; protected $storeManager; public function __construct( \Magento\Framework\App\Helper\Context $context, \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation, \Vendor\Module\Model\Mail\Template\TransportBuilder $transportBuilder, \Magento\Store\Model\StoreManagerInterface $storeManager ) { $this->inlineTranslation = $inlineTranslation; $this->transportBuilder = $transportBuilder; $this->storeManager = $storeManager; parent::__construct($context); } public function generateTemplate() { // path for attachment File $attachmentFile = 'file_path/email.pdf'; $fileName = 'email.pdf'; $emailTemplateVariables['message'] = 'This is a test message by meetanshi.'; //load your email tempate $this->_template = $this->scopeConfig->getValue( self::XML_PATH_EMAIL_DEMO, \Magento\Store\Model\ScopeInterface::SCOPE_STORE, $this->_storeManager->getStore()->getStoreId() ); $this->_inlineTranslation->suspend(); $this->_transportBuilder->setTemplateIdentifier($this->_template) ->setTemplateOptions( [ 'area' => \Magento\Framework\App\Area::AREA_FRONTEND, 'store' => $this->_storeManager->getStore()->getId(), ] ) ->setTemplateVars($emailTemplateVariables) ->setFrom([ 'name' => 'Meetanshi', 'email' => '[email protected]', ]) ->addTo('[email protected]', 'Your Name') ->addAttachment($attachmentFile,$fileName); //Attachment goes here. try { $transport = $this->_transportBuilder->getTransport(); $transport->sendMessage(); $this->_inlineTranslation->resume(); } catch (\Exception $e) { echo $e->getMessage(); die; } }
Method to Add Attachments with Email in Magento 2.3.3:
1. Create di.xml file at app/code/vendor/module/etc/
<preference for="\Magento\Framework\Mail\Template\TransportBuilder" type="Vendor\Module\Model\Mail\Template\TransportBuilder" />
2. Create TransportBuilder.php at app/code/vendor/module/model/mail/template/
<?php declare(strict_types=1); namespace Vendor\Module\Model\Mail\Template; use Magento\Framework\App\TemplateTypesInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\MailException; use Magento\Framework\Mail\EmailMessageInterface; use Magento\Framework\Mail\EmailMessageInterfaceFactory; use Magento\Framework\Mail\AddressConverter; use Magento\Framework\Mail\Exception\InvalidArgumentException; use Magento\Framework\Mail\MessageInterface; use Magento\Framework\Mail\MessageInterfaceFactory; use Magento\Framework\Mail\MimeInterface; 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\TemplateInterface; use Magento\Framework\Mail\TransportInterface; use Magento\Framework\Mail\TransportInterfaceFactory; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Phrase; use Zend\Mime\Mime; use Zend\Mime\PartFactory; /** * TransportBuilder * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 */ class TransportBuilder extends \Magento\Framework\Mail\Template\TransportBuilder { /** * Template Identifier * * @var string */ protected $templateIdentifier; /** * Template Model * * @var string */ protected $templateModel; /** * Template Variables * * @var array */ protected $templateVars; /** * Template Options * * @var array */ protected $templateOptions; /** * Mail Transport * * @var TransportInterface */ protected $transport; /** * Template Factory * * @var FactoryInterface */ protected $templateFactory; /** * Object Manager * * @var ObjectManagerInterface */ protected $objectManager; /** * Message * * @var EmailMessageInterface */ protected $message; /** * Sender resolver * * @var SenderResolverInterface */ protected $_senderResolver; /** * @var TransportInterfaceFactory */ protected $mailTransportFactory; /** * Param that used for storing all message data until it will be used * * @var array */ private $messageData = []; /** * @var EmailMessageInterfaceFactory */ private $emailMessageInterfaceFactory; /** * @var MimeMessageInterfaceFactory */ private $mimeMessageInterfaceFactory; /** * @var MimePartInterfaceFactory */ private $mimePartInterfaceFactory; /** * @var AddressConverter|null */ private $addressConverter; protected $attachments = []; protected $partFactory; /** * TransportBuilder constructor * * @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 * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( 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->objectManager = $objectManager; $this->_senderResolver = $senderResolver; $this->mailTransportFactory = $mailTransportFactory; $this->emailMessageInterfaceFactory = $emailMessageInterfaceFactory ?: $this->objectManager ->get(EmailMessageInterfaceFactory::class); $this->mimeMessageInterfaceFactory = $mimeMessageInterfaceFactory ?: $this->objectManager ->get(MimeMessageInterfaceFactory::class); $this->mimePartInterfaceFactory = $mimePartInterfaceFactory ?: $this->objectManager ->get(MimePartInterfaceFactory::class); $this->addressConverter = $addressConverter ?: $this->objectManager ->get(AddressConverter::class); $this->partFactory = $objectManager->get(PartFactory::class); parent::__construct($templateFactory, $message, $senderResolver, $objectManager, $mailTransportFactory, $messageFactory, $emailMessageInterfaceFactory, $mimeMessageInterfaceFactory, $mimePartInterfaceFactory, $addressConverter); } /** * Add cc address * * @param array|string $address * @param string $name * * @return $this */ public function addCc($address, $name = '') { $this->addAddressByType('cc', $address, $name); return $this; } /** * Add to address * * @param array|string $address * @param string $name * * @return $this * @throws InvalidArgumentException */ public function addTo($address, $name = '') { $this->addAddressByType('to', $address, $name); return $this; } /** * Add bcc address * * @param array|string $address * * @return $this * @throws InvalidArgumentException */ public function addBcc($address) { $this->addAddressByType('bcc', $address); return $this; } /** * Set Reply-To Header * * @param string $email * @param string|null $name * * @return $this * @throws InvalidArgumentException */ public function setReplyTo($email, $name = null) { $this->addAddressByType('replyTo', $email, $name); return $this; } /** * Set mail from address * * @param string|array $from * * @return $this * @throws InvalidArgumentException * @see setFromByScope() * * @deprecated 102.0.1 This function sets the from address but does not provide * a way of setting the correct from addresses based on the scope. */ public function setFrom($from) { return $this->setFromByScope($from); } /** * Set mail from address by scopeId * * @param string|array $from * @param string|int $scopeId * * @return $this * @throws InvalidArgumentException * @throws MailException * @since 102.0.1 */ public function setFromByScope($from, $scopeId = null) { $result = $this->_senderResolver->resolve($from, $scopeId); $this->addAddressByType('from', $result['email'], $result['name']); return $this; } /** * Set template identifier * * @param string $templateIdentifier * * @return $this */ public function setTemplateIdentifier($templateIdentifier) { $this->templateIdentifier = $templateIdentifier; return $this; } /** * Set template model * * @param string $templateModel * * @return $this */ public function setTemplateModel($templateModel) { $this->templateModel = $templateModel; return $this; } /** * Set template vars * * @param array $templateVars * * @return $this */ public function setTemplateVars($templateVars) { $this->templateVars = $templateVars; return $this; } /** * Set template options * * @param array $templateOptions * @return $this */ public function setTemplateOptions($templateOptions) { $this->templateOptions = $templateOptions; return $this; } /** * Get mail transport * * @return TransportInterface * @throws LocalizedException */ public function getTransport() { try { $this->prepareMessage(); $mailTransport = $this->mailTransportFactory->create(['message' => clone $this->message]); } finally { $this->reset(); } return $mailTransport; } /** * Reset object state * * @return $this */ protected function reset() { $this->messageData = []; $this->templateIdentifier = null; $this->templateVars = null; $this->templateOptions = null; return $this; } /** * Get template * * @return TemplateInterface */ protected function getTemplate() { return $this->templateFactory->get($this->templateIdentifier, $this->templateModel) ->setVars($this->templateVars) ->setOptions($this->templateOptions); } /** * Prepare message. * * @return $this * @throws LocalizedException if template type is unknown */ protected function prepareMessage() { $template = $this->getTemplate(); $content = $template->processTemplate(); switch ($template->getType()) { case TemplateTypesInterface::TYPE_TEXT: $part['type'] = MimeInterface::TYPE_TEXT; break; case TemplateTypesInterface::TYPE_HTML: $part['type'] = MimeInterface::TYPE_HTML; break; default: throw new LocalizedException( new Phrase('Unknown template type') ); } $mimePart = $this->mimePartInterfaceFactory->create(['content' => $content]); $parts = count($this->attachments) ? array_merge([$mimePart], $this->attachments) : [$mimePart]; $this->messageData['body'] = $this->mimeMessageInterfaceFactory->create( ['parts' => $parts] ); $this->messageData['subject'] = html_entity_decode( (string)$template->getSubject(), ENT_QUOTES ); $this->message = $this->emailMessageInterfaceFactory->create($this->messageData); return $this; } /** * Handles possible incoming types of email (string or array) * * @param string $addressType * @param string|array $email * @param string|null $name * * @return void * @throws InvalidArgumentException */ private function addAddressByType(string $addressType, $email, ?string $name = null): void { if (is_string($email)) { $this->messageData[$addressType][] = $this->addressConverter->convert($email, $name); return; } $convertedAddressArray = $this->addressConverter->convertMany($email); if (isset($this->messageData[$addressType])) { $this->messageData[$addressType] = array_merge( $this->messageData[$addressType], $convertedAddressArray ); } } /** * @param string|null $content * @param string|null $fileName * @param string|null $fileType * @return TransportBuilder */ public function addAttachment(?string $content, ?string $fileName, ?string $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; } }
Add attachment in email like :
$this->transportBuilder->addAttachment($content, $fileName, 'application/pdf');
That’s all you need to do for implementing Emails with attachments in Magento 2.3.x
You may share the above solution with the Magento community via social media.
Thanks.