Sending an email with PDF attachment in Magento
in this script we will mention two ways to add an attachment to an email in Magento2, one is for Magento 2.4.6 and less and the another one for Magento < 2.4.6 or 2.4.8 in specifc
first of all override the templatebuilder clas
in the di.xml add his
<preference for="Magento\Framework\Mail\Template\TransportBuilder" type="Vendor\Module\Rewrite\Magento\Framework\Mail\Template\TransportBuilder"/>
now add this class (Magento 2.4.8)
<?php
declare(strict_types=1);
namespace Vendor\Module\Rewrite\Magento\Framework\Mail\Template;
use Magento\Framework\App\TemplateTypesInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\MailException;
use Magento\Framework\Mail\AddressConverter;
use Magento\Framework\Mail\EmailMessageInterfaceFactory;
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\TemplateInterface;
use Magento\Framework\Mail\TransportInterface;
use Magento\Framework\Mail\TransportInterfaceFactory;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Phrase;
use Magento\Framework\Mail\Template\FactoryInterface;
use Magento\Framework\Mail\Template\SenderResolverInterface;
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 \Magento\Framework\Mail\Template\FactoryInterface
*/
protected $templateFactory;
/**
* Object Manager
*
* @var ObjectManagerInterface
*/
protected $objectManager;
/**
* Message
*
* @var MessageInterface
*/
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;
private $messageBodyParts = [];
/**
* TransportBuilder constructor
*
* @param \Magento\Framework\Mail\Template\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(
\Magento\Framework\Mail\Template\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
) {
parent::__construct( $templateFactory,
$message,
$senderResolver,
$objectManager,
$mailTransportFactory,
$messageFactory = null,
$emailMessageInterfaceFactory = null,
$mimeMessageInterfaceFactory = null,
$mimePartInterfaceFactory = null,
$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);
}
/**
* 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 prepareMessageOriginal()
{
$template = $this->getTemplate();
$content = $template->processTemplate();
switch ($template->getType()) {
case TemplateTypesInterface::TYPE_TEXT:
$partType = MimeInterface::TYPE_TEXT;
break;
case TemplateTypesInterface::TYPE_HTML:
$partType = MimeInterface::TYPE_HTML;
break;
default:
throw new LocalizedException(
new Phrase('Unknown template type')
);
}
/** @var \Magento\Framework\Mail\MimePartInterface $mimePart */
$mimePart = $this->mimePartInterfaceFactory->create(
[
'content' => $content,
'type' => $partType
]
);
$this->messageData['encoding'] = $mimePart->getCharset();
$this->messageData['body'] = $this->mimeMessageInterfaceFactory->create(
['parts' => [$mimePart]]
);
$this->messageData['subject'] = html_entity_decode(
(string)$template->getSubject(),
ENT_QUOTES
);
$this->message = $this->emailMessageInterfaceFactory->create($this->messageData);
return $this;
}
/**
* @inheritdoc
*/
public function addItmAttachment(
$body,
$filename = null,
$mimeType = \Magento\Framework\HTTP\Mime::TYPE_OCTETSTREAM
) {
$this->messageBodyParts[] = new \Symfony\Component\Mime\Part\DataPart(
$body,
$filename,
$mimeType,
\Magento\Framework\HTTP\Mime::ENCODING_BASE64
);
return $this;
}
/**
* Prepare message.
*
* @return \Magento\Framework\Mail\Template\TransportBuilder
* @throws LocalizedException if template type is unknown
*/
protected function prepareMessage()
{
$template = $this->getTemplate();
$content = $template->processTemplate();
switch ($template->getType()) {
case TemplateTypesInterface::TYPE_TEXT:
$partType = MimeInterface::TYPE_TEXT;
break;
case TemplateTypesInterface::TYPE_HTML:
$partType = MimeInterface::TYPE_HTML;
break;
default:
throw new LocalizedException(
new Phrase('Unknown template type')
);
}
/** @var \Magento\Framework\Mail\MimePartInterface $mimePart */
$mimePart = $this->mimePartInterfaceFactory->create(
[
'content' => $content,
'type' => $partType
]
);
$this->messageData['encoding'] = $mimePart->getCharset();
$this->messageData['body'] = $this->mimeMessageInterfaceFactory->create(
['parts' => [$mimePart]]
);
$this->messageData['subject'] = html_entity_decode(
(string)$template->getSubject(),
ENT_QUOTES
);
$this->message = $this->emailMessageInterfaceFactory->create($this->messageData);
if (count($this->messageBodyParts)) {
$this->messageData['body']->getMimeMessage()->setBody(
new \Symfony\Component\Mime\Part\Multipart\MixedPart($this->messageData['body']->getMimeMessage()->getBody(), ...$this->messageBodyParts)
);
}
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
);
} else {
$this->messageData[$addressType] = $convertedAddressArray;
}
}
}
then you can use it like this
private function sendProductPdfEmail($pdfPath, $email, $product_name)
{
$fileName = basename($pdfPath);
$senderInfo = [
'name' => $this->getStorename(),
'email' => $this->getStoreEmail()
];
$email_template = $this->getProductDetailsEmailTemplate();
$transportBuilder = $this->objectManager
->get('Magento\Framework\Mail\Template\TransportBuilder');
$transport = $transportBuilder
->setTemplateIdentifier($email_template)
->setTemplateOptions([
'area' => \Magento\Framework\App\Area::AREA_FRONTEND,
'store' => 1
])
->setTemplateVars(["product_name" => $product_name])
->addItmAttachment(file_get_contents($pdfPath), $fileName, 'application/pdf')
->setFrom($senderInfo)
->addTo($email)
->getTransport();
try {
$transport->sendMessage();
} catch (\Magento\Framework\Exception\MailException $ex) {
return __("Error Sending Email. Please contact your administrator for assistance");
}
return true;
}now in Magento 2.4.6 we can use this class
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Vendor\Module\Rewrite\Magento\Framework\Mail\Template;
use Magento\Framework\App\TemplateTypesInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\MailException;
use Magento\Framework\Mail\AddressConverter;
use Magento\Framework\Mail\EmailMessageInterfaceFactory;
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\TemplateInterface;
use Magento\Framework\Mail\TransportInterface;
use Magento\Framework\Mail\TransportInterfaceFactory;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Phrase;
use Magento\Framework\Mail\Template\FactoryInterface;
use Magento\Framework\Mail\Template\SenderResolverInterface;
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 MessageInterface
*/
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;
private $attachments = [];
/**
* 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
) {
parent::__construct( $templateFactory,
$message,
$senderResolver,
$objectManager,
$mailTransportFactory,
$messageFactory = null,
$emailMessageInterfaceFactory = null,
$mimeMessageInterfaceFactory = null,
$mimePartInterfaceFactory = null,
$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);
}
public function addAttachments($attachements)
{
$this->attachments = [];
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
foreach ($attachements as $attachement) {
$partFactory = $objectManager->create(\Laminas\Mime\PartFactory::class);
$attachmentPart = $partFactory->create();
$attachmentPart->setContent($attachement["content"])
->setType($attachement["fileType"])
->setFileName($attachement["fileName"])
->setDisposition(\Laminas\Mime\Mime::DISPOSITION_ATTACHMENT)
->setEncoding(\Laminas\Mime\Mime::ENCODING_BASE64);
$this->attachments[] = $attachmentPart;
}
return $this;
}
/**
* Prepare message.
*
* @return \Magento\Framework\Mail\Template\TransportBuilder
* @throws LocalizedException if template type is unknown
*/
protected function prepareMessage()
{
$template = $this->getTemplate();
$content = $template->processTemplate();
switch ($template->getType()) {
case \Magento\Framework\App\TemplateTypesInterface::TYPE_TEXT:
$partType = \Magento\Framework\Mail\MimeInterface::TYPE_TEXT;
break;
case \Magento\Framework\App\TemplateTypesInterface::TYPE_HTML:
$partType = \Magento\Framework\Mail\MimeInterface::TYPE_HTML;
break;
default:
throw new \Magento\Framework\Exception\LocalizedException(
new \Magento\Framework\Phrase('Unknown template type')
);
}
/** @var \Magento\Framework\Mail\MimePartInterface $mimePart */
$mimePart = $this->mimePartInterfaceFactory->create(
[
'content' => $content,
'type' => $partType
]
);
$partsWithAttachments = count($this->attachments) ? array_merge([$mimePart], $this->attachments) : [$mimePart];
$this->messageData['encoding'] = $mimePart->getCharset();
$this->messageData['body'] = $this->mimeMessageInterfaceFactory->create(
['parts' => $partsWithAttachments]
);
$this->messageData['subject'] = html_entity_decode(
(string)$template->getSubject(),
ENT_QUOTES
);
$this->message = $this->emailMessageInterfaceFactory->create($this->messageData);
return $this;
}
/**
* Add cc address
*
* @param array|string $address
* @param string $name
*
* @return \Magento\Framework\Mail\Template\TransportBuilder
*/
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);
}
/**
* 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
);
} else {
$this->messageData[$addressType] = $convertedAddressArray;
}
}
}