In Magento version 2.4.7, I was able to attach files to emails using the laminas-mime library. However, starting from Magento 2.4.8, the laminas-mime library has been removed, and the methods that worked earlier no longer function.
I’ve already referred to the following resources: Magento 2: send email with Attachment, https://webkul.com/blog/attach-pdf-file-magento-2-email/, https://meetanshi.com/blog/add-attachments-with-email-in-magento-2-3-x/
Unfortunately, none of these solutions are working in Magento 2.4.8.
How can I add an attachment to an email in Magento 2.4.8 now that laminas-mime has been removed? Is there a new recommended way to handle this in the latest version?
Thanks in advance!
2 Answers 2
Create di.xml at path Vendor/Module/etc/
<?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="Vendor\Module\Model\Mail\Template\Attachment" />
</config>
Create Attachment.php at path Vendor/Module/Model/Mail/Template/
<?php
namespace Vendor\Module\Model\Mail\Template;
use Magento\Framework\HTTP\Mime;
use Magento\Framework\Mail\Template\FactoryInterface;
use Magento\Framework\Mail\Template\SenderResolverInterface;
use Magento\Framework\Mail\Template\TransportBuilder;
use Magento\Framework\App\TemplateTypesInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Mail\AddressConverter;
use Magento\Framework\Mail\Exception\InvalidArgumentException;
use Magento\Framework\Exception\MailException;
use Magento\Framework\Mail\MessageInterface;
use Magento\Framework\Mail\MimeInterface;
use Magento\Framework\Mail\EmailMessageInterfaceFactory;
use Magento\Framework\Mail\MimeMessageInterfaceFactory;
use Magento\Framework\Mail\MimePartInterfaceFactory;
use Magento\Framework\Mail\TransportInterfaceFactory;
use Magento\Framework\ObjectManagerInterface;
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\Multipart\MixedPart;
class Attachment extends TransportBuilder
{
/**
* 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
*/
private $addressConverter;
/**
* @var string
*/
private $messageBodyParts = [];
/**
* @param FactoryInterface $templateFactory
* @param MessageInterface $message
* @param SenderResolverInterface $senderResolver
* @param ObjectManagerInterface $objectManager
* @param TransportInterfaceFactory $mailTransportFactory
* @param EmailMessageInterfaceFactory $emailMessageInterfaceFactory
* @param MimeMessageInterfaceFactory $mimeMessageInterfaceFactory
* @param MimePartInterfaceFactory $mimePartInterfaceFactory
* @param AddressConverter $addressConverter
*/
public function __construct(
FactoryInterface $templateFactory,
MessageInterface $message,
SenderResolverInterface $senderResolver,
ObjectManagerInterface $objectManager,
TransportInterfaceFactory $mailTransportFactory,
EmailMessageInterfaceFactory $emailMessageInterfaceFactory,
MimeMessageInterfaceFactory $mimeMessageInterfaceFactory,
MimePartInterfaceFactory $mimePartInterfaceFactory,
AddressConverter $addressConverter
) {
parent::__construct(
$templateFactory,
$message,
$senderResolver,
$objectManager,
$mailTransportFactory
);
$this->emailMessageInterfaceFactory = $emailMessageInterfaceFactory;
$this->mimeMessageInterfaceFactory = $mimeMessageInterfaceFactory;
$this->mimePartInterfaceFactory = $mimePartInterfaceFactory;
$this->addressConverter = $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;
}
/**
* Reset object state
*
* @return $this
*/
protected function reset()
{
$this->messageData = [];
$this->templateIdentifier = null;
$this->templateVars = null;
$this->templateOptions = null;
$this->messageBodyParts = [];
return $this;
}
/**
* 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:
$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 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;
}
}
/**
* @inheritdoc
*/
public function addAttachment(
$body,
$filename = null,
$mimeType = Mime::TYPE_OCTETSTREAM
) {
$this->messageBodyParts[] = new DataPart(
$body,
$filename,
$mimeType,
Mime::ENCODING_BASE64
);
return $this;
}
}
Use addAttachment method when send email
try {
$transport = $this->transportBuilder->setTemplateIdentifier([TEMPLATE_NAME])
->setTemplateOptions(['area' => 'frontend', 'store' => [STORE_ID]])
->setTemplateVars([EMAIL_TEMPLATE_VARIABLES])
->setFrom([SENDER_INFO])
->addTo([RECEIVER_INFO]);
$fileContent = file_get_contents([FILE_PATH]);
$transport = $this->transportBuilder->addAttachment($fileContent, [FILE_NAME]);
$transport = $this->transportBuilder->getTransport();
$transport->sendMessage();
} catch (\Exception $e) {
$this->managerInterface->addErrorMessage(__('Email send fail'));
}
You can follow this code to send emails in Magento 2.4.8. I am using Symfony\Component\Mime.
<?php
require __DIR__ . '/app/bootstrap.php';
use Magento\Framework\App\Bootstrap;
use Magento\Framework\Mail\Template\TransportBuilder;
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\Multipart\MixedPart;
use Symfony\Component\Mime\Part\TextPart;
$bootstrap = Bootstrap::create(BP, $_SERVER);
$objectManager = $bootstrap->getObjectManager();
// Set area code before using TransportBuilder
$appState = $objectManager->get(\Magento\Framework\App\State::class);
try {
$appState->setAreaCode(\Magento\Framework\App\Area::AREA_FRONTEND);
} catch (\Magento\Framework\Exception\LocalizedException $e) {
// Area code is already set, ignore
}
// Prepare PDF content (replace with your actual PDF file path)
$pdfPath = '/var/www/html/sample.pdf';
$pdfContent = file_get_contents($pdfPath);
// Create the text part
$textPart = new TextPart('Hello, this is the email body.', 'utf-8', 'plain');
// Create the PDF attachment part
$pdfPart = new DataPart($pdfContent, 'attachment.pdf', 'application/pdf');
// Combine parts into a mixed part (body + attachment)
$body = new MixedPart($textPart, $pdfPart);
// Use TransportBuilder to send email with attachment
/** @var TransportBuilder $transportBuilder */
$transportBuilder = $objectManager->get(TransportBuilder::class);
// Use a valid template identifier, e.g., Magento_Email/email_template_generic
$transportBuilder->setTemplateIdentifier('sales_email_order_template')
->setTemplateOptions([
'area' => \Magento\Framework\App\Area::AREA_FRONTEND,
'store' => 1
])
->setTemplateVars(['message' => 'Hello, this is the email body.'])
->setFrom(['email' => '[email protected]', 'name' => 'Sender'])
->addTo('[email protected]', 'Recipient');
// Get the transport and then the message to access SymfonyMessage
try {
$transport = $transportBuilder->getTransport();
} catch (\Exception $e) {
echo $e->getMessage();
echo $e->getTraceAsString();
exit;
}
$email = $transport->getMessage();
$email->getSymfonyMessage()->setBody($body);
// Send the email
$transport->sendMessage();
echo "Email sent with PDF attachment.\n";
-
Thank you for the solution. However, I'm seeking an implementation that adheres to Magento's best practices. I’ve already come across this approach.Rutvik Monpara– Rutvik Monpara2025年05月15日 09:08:36 +00:00Commented May 15 at 9:08
-
That's right. The code above runs but it's only for testing purposes. The key point here is that you need to use: DataPart,Multipart\MixedPart and TextPart to be able to send mail on Magento 2.4.8. Also, you need to create a class and a constructor for it so that it can run properly.Mageplaza-CS– Mageplaza-CS2025年05月16日 02:01:29 +00:00Commented May 16 at 2:01
Explore related questions
See similar questions with these tags.