2

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!

asked May 14 at 8:26

2 Answers 2

3

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'));
}
answered May 15 at 13:12
0

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";
answered May 15 at 8:54
2
  • 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. Commented 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. Commented May 16 at 2:01

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.