2

I am working on a module in which I need to attach an attachment with sales email. However, Magento 2.3 use zendframework2 so email attachment is not the same as zendframework1 (in Magento 2.2).

Can anyone help me to achieve the same task? How to implement attachment functions in Magento 2.3

I have searched in google and read several questions but I didn't find the solution.

3

5 Answers 5

1

Fixed this issue by extending Magento\Framework\Mail\Message and adding new function createCustomAttachment in this, also edited the createHtmlMimeFromString function.

protected $attachment;

public function createCustomAttachment($body, $mimeType, $disposition, $encoding, $filename){
    $attachment = new Part($body);
    $attachment->setType($mimeType);
    $attachment->setDisposition($disposition);
    $attachment->setEncoding($encoding);
    $attachment->setFileName($filename);
    $this->attachment = $attachment;
    return $this;
}

Called the global variable $this->attachment in the function createHtmlMimeFromString. If the variable has value then we are adding the attachment data to the addPart function.

The code be like this

private function createHtmlMimeFromString($htmlBody)
{
    $htmlPart = new Part($htmlBody);
    $htmlPart->setCharset($this->zendMessage->getEncoding());
    $htmlPart->setType(Mime::TYPE_HTML);
    $mimeMessage = new \Zend\Mime\Message();
    $mimeMessage->addPart($htmlPart);
    if ($this->attachment) {
        $mimeMessage->addPart($this->attachment);
    }

    return $mimeMessage;
}

We need to copy the Magento\Framework\Mail\Message entire content in the extended file because the zendMessage is private and this is called in almost all functions.

We can call the createCustomAttachment function from the transport builder to pass the attachment details.

public function addAttachment($body,
                              $mimeType = Mime::TYPE_OCTETSTREAM,
                              $disposition = Mime::DISPOSITION_ATTACHMENT,
                              $encoding = Mime::ENCODING_BASE64,
                              $filename = null)
{
    //$this->message->createAttachment($body, $mimeType, $disposition, $encoding, $filename);
    $this->message->createCustomAttachment($body, $mimeType, $disposition, $encoding, $filename);
    return $this;
}
7

Try this way

Some of the functionalities in Magento 2.3.x is different from previous version.
The reason is that in most cases the 2.3.x framework uses Zend Framework 2 (ZF2) and almost totally refuses to applyZend Framework 1 (ZF1).

Magento\Framework\Mail\MailMessageInterface::createAttachment()


So, due to this, the previous method is deprecated and there is no direct method to do so.

As createAttachment() method is no more available, we will override Magento\Framework\Mail\Message and create a class which will implement Magento\Framework\Mail\MailMessageInterface.


[vendor_name] / [Module_name] /Mail

Message.php

<?php

namespace [vendor_name]\[Module_name]\Mail;

use Magento\Framework\Mail\MailMessageInterface;
use Zend\Mail\MessageFactory as MailMessageFactory;
use Zend\Mime\MessageFactory as MimeMessageFactory;
use Zend\Mime\Mime;
use Zend\Mime\Part;
use Zend\Mime\PartFactory;


class Message implements MailMessageInterface
{
    protected $partFactory;
    protected $mimeMessageFactory;
    private $zendMessage;
    private $messageType = self::TYPE_TEXT;
    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)
    {
        $this->setMessageType(self::TYPE_TEXT);
        $textPart = $this->partFactory->create();

        $textPart->setContent($content)
            ->setType(Mime::TYPE_TEXT)
            ->setCharset($this->zendMessage->getEncoding());

        $this->parts[] = $textPart;

        return $this;
    }
    public function setBodyHtml($content)
    {
        $this->setMessageType(self::TYPE_HTML);
        $htmlPart = $this->partFactory->create();

        $htmlPart->setContent($content)
            ->setType(Mime::TYPE_HTML)
            ->setCharset($this->zendMessage->getEncoding());

        $this->parts[] = $htmlPart;

        $mimeMessage = new \Zend\Mime\Message();
        $mimeMessage->addPart($htmlPart);
        $this->zendMessage->setBody($mimeMessage);

        return $this;
    }
    public function setBodyAttachment($content, $fileName, $fileType, $encoding = '8bit')
    {
        $attachmentPart = $this->partFactory->create();

        $attachmentPart->setContent($content)
            ->setType($fileType)
            ->setFileName($fileName)
            ->setDisposition(Mime::DISPOSITION_ATTACHMENT)
            ->setEncoding($encoding);

        $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)
    {
        if (is_string($body) && $this->messageType === self::TYPE_HTML) {
            $body = self::createHtmlMimeFromString($body);
        }
        $this->zendMessage->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->setFromAddress($fromAddress, null);

        return $this;
    }
    public function setFromAddress($fromAddress, $fromName = null)
    {
        $this->zendMessage->setFrom($fromAddress, $fromName);

        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();
    }
    private function createHtmlMimeFromString($htmlBody)
    {
        $htmlPart = new Part($htmlBody);
        $htmlPart->setCharset($this->zendMessage->getEncoding());
        $htmlPart->setType(Mime::TYPE_HTML);
        $mimeMessage = new \Zend\Mime\Message();
        $mimeMessage->addPart($htmlPart);

        return $mimeMessage;
    }
    public function setMessageType($type)
    {
        $this->messageType = $type;

        return $this;
    }
}

After implementation of the Mail\Message class we need to extend the \Magento\Framework\Mail\Template\TransportBuilder class.
This class is used for building the \Magento\Framework\Mail\Transport class which is in turn used for email sending.

[Vendor_name] / [Module_name] /Model/Mail/Template

AddEmailAttachemnt.php

<?php
namespace [Vendor_name]\[module_name]\Model\Mail\Template;

class AddEmailAttachemnt extends \Magento\Framework\Mail\Template\TransportBuilder
{

    public function addAttachment($file_content,$file_name,$file_type) 
    {
        $encoding = \Zend_Mime::ENCODING_BASE64;
        $this->message->setBodyAttachment($file_content, $file_name, $file_type, $encoding);
        return $this;
    }
    protected function prepareMessage()
    {
        parent::prepareMessage();
        $this->message->setPartsToBody();
        return $this;
    }
}

*Here value of Function Parameter*

**$file_content**
$file_url = "Exiting File URL (WEB-URL)";
$file_content = file_get_contents($file_url);

**$file_name**
this is name of file with extension like we have PDF file so name is **FileName.pdf**

**$file_type**
There are many File type for Different Files here are some File type list
'txt'  => 'text/plain',
'pdf'  => 'application/pdf',
'png' => 'image/png'

Click Here to More File Type


**$encoding**
*here is Encoding type value of File on Internet if you are not pass this argument by default is 6-bit Encoding is apply. this value is depends on your file.*

Now you Have to add below Lines to your di.xml file which are call your Overwrite files.

[vendor_name] / [Module_name] / 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">

   <preference for="\Magento\Framework\Mail\Template\TransportBuilder" type="[Vendor_name]\[module_name]\Model\Mail\Template\AddEmailAttachemnt" />
   
   <preference for="Magento\Framework\Mail\Message" type="[Vendor_name]\[module_name]\Mail\Message"/>

</config>

so now all file are place properly so now we will use send Email Attechment

Add this Code to send Email With Attechment

protected $_transportBuilder;

 public function __construct(
        ...................................................................
        \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder,
        ....................................................................
   ) {
       .................................
       .................................
       $this->_transportBuilder = $transportBuilder;
      ..................................... 
   }
   public function execute() {
   
        ...........................
        ...........................
        ...........................

         $transport = $this->_transportBuilder->setTemplateIdentifier($Email_template_name) // put Email Template Name
              ->setTemplateOptions(['area' => 'frontend', 'store' => $store]) // $store is Current Store ID 
              ->setTemplateVars(
                    [
                       'var_1' => 'value_1',
                       'var_2' => 'value_2',
                       'var_3'    => 'value_3'
                    ]
                  )
              ->setFrom('[email protected]') // Sender Email address
              ->addTo('[email protected]') // Receiver Email Address
              ->addAttachment($file_content, $file_name, $extension) // here Addtement are add with Email
              ->getTransport();
          $transport->sendMessage();

        ............................
        ............................
        ............................
   }

Now All code are Place at the Right place now run This Command

php bin/magento s:up

php bin/magento s:s:d -f

php bin/magento c:c

php bin/magento c:f

Now check your Receiver Email address attachment

Update in Magento [2.3.3]

Mangento 2.3.3 release introduces a new, immutable EmailMessageInterface that supports the sending of multi-part MIME-type content in email.
The Magento\Framework\Mail\Template\TransportBuilder and Magento\Newsletter\Model\Queue\TransportBuilder structures were refactored to use this new EmailMessageInterface instead of MessageInterface, which was previously used.
If you are a Magento extension developer and rely on \Magento\Email\Model\Transport::getMessage() or \Magento\Framework\Mail\TransportInterface::getMessage(), those methods will now return the new EmailMessageInterface.

Here add below Lines to your di.xml file which are call your Overwrite files.

[vendor_name] / [Module_name] / 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">

   <preference for="\Magento\Framework\Mail\Template\TransportBuilder" type="[Vendor_name]\[module_name]\Model\Mail\Template\AddEmailAttachemnt" />


</config>

[Vendor_name] / [Module_name] /Model/Mail/Template

AddEmailAttachemnt.php

<?php
declare (strict_types = 1);

namespace [Vendor_name]\[module_name]\Model\Mail\Template;

use Magento\Framework\App\TemplateTypesInterface;
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\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\TransportInterfaceFactory;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Phrase;
use Zend\Mime\Mime;
use Zend\Mime\PartFactory;

class AddEmailAttachemnt extends \Magento\Framework\Mail\Template\TransportBuilder
{

    protected $templateIdentifier;
    protected $templateModel;
    protected $templateVars;
    protected $templateOptions;
    protected $transport;
    protected $templateFactory;
    protected $objectManager;
    protected $message;
    protected $_senderResolver;
    protected $mailTransportFactory;
    private $messageData = [];
    private $emailMessageInterfaceFactory;
    private $mimeMessageInterfaceFactory;
    private $mimePartInterfaceFactory;
    private $addressConverter;
    protected $attachments = [];
    protected $partFactory;

    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
        );
    }
    public function addCc($address, $name = '')
    {
        $this->addAddressByType('cc', $address, $name);

        return $this;
    }
    public function addTo($address, $name = '')
    {
        $this->addAddressByType('to', $address, $name);

        return $this;
    }
    public function addBcc($address)
    {
        $this->addAddressByType('bcc', $address);

        return $this;
    }
    public function setReplyTo($email, $name = null)
    {
        $this->addAddressByType('replyTo', $email, $name);

        return $this;
    }
    public function setFrom($from)
    {
        return $this->setFromByScope($from);
    }
    public function setFromByScope($from, $scopeId = null)
    {
        $result = $this->_senderResolver->resolve($from, $scopeId);
        $this->addAddressByType('from', $result['email'], $result['name']);

        return $this;
    }
    public function setTemplateIdentifier($templateIdentifier)
    {
        $this->templateIdentifier = $templateIdentifier;

        return $this;
    }
    public function setTemplateModel($templateModel)
    {
        $this->templateModel = $templateModel;
        return $this;
    }
    public function setTemplateVars($templateVars)
    {
        $this->templateVars = $templateVars;

        return $this;
    }
    public function setTemplateOptions($templateOptions)
    {
        $this->templateOptions = $templateOptions;

        return $this;
    }
    public function getTransport()
    {
        try {
            $this->prepareMessage();
            $mailTransport = $this->mailTransportFactory->create(['message' => clone $this->message]);
        } finally {
            $this->reset();
        }

        return $mailTransport;
    }
    protected function reset()
    {
        $this->messageData = [];
        $this->templateIdentifier = null;
        $this->templateVars = null;
        $this->templateOptions = null;
        return $this;
    }
    protected function getTemplate()
    {
        return $this->templateFactory->get($this->templateIdentifier, $this->templateModel)
            ->setVars($this->templateVars)
            ->setOptions($this->templateOptions);
    }
    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;
    }
    private function addAddressByType($addressType, $email, $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
            );
        }
    }
    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;
    }
}

Add this Code to send Email With Attechment

protected $_transportBuilder;

 public function __construct(
        ...................................................................
        \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder,
        ....................................................................
   ) {
       .................................
       .................................
       $this->_transportBuilder = $transportBuilder;
      ..................................... 
   }
   public function execute() {
   
        ...........................
        ...........................
        ...........................

         $transport = $this->_transportBuilder->setTemplateIdentifier($Email_template_name) // put Email Template Name
              ->setTemplateOptions(['area' => 'frontend', 'store' => $store]) // $store is Current Store ID 
              ->setTemplateVars(
                    [
                       'var_1' => 'value_1',
                       'var_2' => 'value_2',
                       'var_3'    => 'value_3'
                    ]
                  )
              ->setFrom('[email protected]') // Sender Email address
              ->addTo('[email protected]') // Receiver Email Address
              ->addAttachment($file_content, $file_name, $extension) // here Addtement are add with Email
              ->getTransport();
          $transport->sendMessage();

        ............................
        ............................
        ............................
   }

Now All code are Place at the Right place now run This Command

php bin/magento s:up

php bin/magento s:s:d -f

php bin/magento c:c

php bin/magento c:f

Update in Magento [2.4]

Laminas is simply the next step in the evolution of the Zend Framework. Only the naming and governance of the framework has changed.

Anyone can still install all existing Zend Framework, Apigility, and Expressive packages. However, these versions will no longer be maintained by the community.Click here

Use code into Update in Magento [2.3.3]

Replace this Class

use Zend\Mime\Mime;
use Zend\Mime\PartFactory;

With this Class

use Laminas\Mime\Mime;
use Laminas\Mime\PartFactory;

Remaining code are same into Update in Magento [2.3.3]

Now check your Receiver Email address for attachment

I Hope This Helps You.

2
  • This does work in 2.4. Much thanks Commented Jan 21, 2021 at 17:29
  • In 2.4 only namespace is change not code. Replae Zend to Laminas only. please read above answer.
    – Msquare
    Commented Jan 22, 2021 at 6:51
1

This solution is working so charming

 $transport = $_transportBuilder->setTemplateIdentifier(20)
->setTemplateOptions($templateOptions)
->setTemplateVars($templateVars)
->setFrom($from)
->addTo($vendor_email)
->getTransport();

 $html= $transport->getMessage()->getBody()->generateMessage();            
$bodyMessage = new \Zend\Mime\Part($html);
$bodyMessage->type = 'text/html';
$attachment=$_transportBuilder->addAttachment($pdfData,$fileName);      
$bodyPart = new \Zend\Mime\Message();
$bodyPart->setParts(array($bodyMessage,$attachment));
$transport->getMessage()->setBody($bodyPart);                
$transport->sendMessage();
$inlineTranslation->resume();
0

I had the same issue.

To solve this i created a helper.

use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Mail\MessageInterface;
use Magento\Framework\Mail\Template\FactoryInterface;
use Magento\Framework\Mail\Template\SenderResolverInterface;
use Magento\Framework\Mail\TransportInterfaceFactory;   
use Magento\Framework\Mail\Template\TransportBuilder;

class UploadTransportBuilder extends TransportBuilder {

public function __construct(FactoryInterface $templateFactory,
    MessageInterface $message,
    SenderResolverInterface $senderResolver,
    ObjectManagerInterface $objectManager,
    TransportInterfaceFactory $mailTransportFactory) {
    parent::__construct($templateFactory,
        $message,
        $senderResolver,
        $objectManager,
        $mailTransportFactory);
}

public function attachFile(
    $body,
    $filename    = null,
    $mimeType    = \Zend_Mime::TYPE_OCTETSTREAM,
    $disposition = \Zend_Mime::DISPOSITION_ATTACHMENT,
    $encoding    = \Zend_Mime::ENCODING_BASE64
)
{
    $this->message->createAttachment($body, $mimeType, $disposition, $encoding, $filename);
    return $this;
}

}

Then I loaded this on my controller.

use BVZ\Wishlist\Helper\UploadTransportBuilder;

UploadTransportBuilder $transportBuilder
$this->_transportBuilder = $transportBuilder

$transport = $this->_transportBuilder->setTemplateIdentifier('bvz_customer_wishlist')
            ->setTemplateOptions(['area' => 'frontend', 'store' => $store])
            ->setTemplateVars(['data' => $postObject])
            ->setFrom('support')
            ->addTo($formEmail, $customerName)
            ->attachFile($pdf,'Seleção de Produtos.pdf')
            ->getTransport();

$this->messageManager->addSuccess(__('Um e-mail foi enviado com sua lista de desejo.'));

            $transport->sendMessage();

The code method for attach a file is attachFile();

3
  • Thank you @Gabriel Fernandes I will try this. Is it working in magento 2.3 ? Commented Jan 8, 2019 at 13:39
  • i'm using 2.2.3, maybe works on 2.3 Commented Jan 8, 2019 at 13:42
  • Thanks @Gabriel. Magento 2.3 using zendframework2 & zendframework had deleted createAttachment() function so it is not working in 2.3 however older magento 2 version use zendframework1 so It is working in those versions error: prnt.sc/m4gr1e Commented Jan 8, 2019 at 13:54
0

Send Email with Attachment in Magento 2.3

https://extait.com/blog/how-to-send-email-with-attachment-in-magento-2-3/

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.