Magento 2: envie um email com anexo


Respostas:


19

O M2 não vem pronto para uso, no entanto, é um recurso incorporado à estrutura do zend. Aqui está uma boa referência de como adicionar essa funcionalidade ao magento: https://blog.bitexpert.de/blog/sending-mails-with-attachments-in-magento-2/

Caso o link fique inoperante, crie o seguinte

<?php
namespace Your\CustomModule\Magento\Mail\Template;

class TransportBuilder 
    extends \Magento\Framework\Mail\Template\TransportBuilder
{
    public function addAttachment(
        $body,
        $mimeType    = Zend_Mime::TYPE_OCTETSTREAM,
        $disposition = Zend_Mime::DISPOSITION_ATTACHMENT,
        $encoding    = Zend_Mime::ENCODING_BASE64,
        $filename    = null
    ) {
        $this->message->createAttachment($body, $mimeType, $disposition, $encoding, $filename);
        return $this;
    }
}

depois adicione a 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="\Your\CustomModule\Magento\Mail\Template\TransportBuilder" />
</config>

Agora você pode usar em addAttachment()todo o site.


8
Eu ainda me pergunto por que magento TransportBuilder não tem esse método
Murtuza Zabuawala

4
Como podemos anexar um arquivo no email personalizado magento 2.3? porque a sua utilização ZendFramework 2 e esta resposta não funciona mais
Manish Maheshwari

3
Como enviar email com anexo no Magento 2.3?
Dhaduk Mitesh

@ManishMaheshwari & Mitesh Você tem a solução?
Sameer Bhayani

1
Esta solução não está mais funcionando no Magento2.3. Alguém tem alternativa para apego.?
Nishu

8

A partir do Magento 2.2.7, as soluções descritas acima não funcionam mais desde que a \Magento\Framework\Mail\Messageextensão caiu \Zend_Mail.
Para contornar a falta de uma maneira fácil de adicionar anexos por meio do construtor de transporte (que atualmente parece ser o local correto para essa função), é necessário criar um substituto para o TransportBuilder e usar \Zend\Mime\Part:

<?php
namespace Your\CustomModule\Magento\Mail\Template;

use Magento\Framework\Mail\MessageInterface;
use Magento\Framework\Mail\MessageInterfaceFactory;
use Magento\Framework\Mail\Template\FactoryInterface;
use Magento\Framework\Mail\Template\SenderResolverInterface;
use Magento\Framework\Mail\TransportInterfaceFactory;
use Magento\Framework\ObjectManagerInterface;
use Zend\Mime\Mime;
use Zend\Mime\Part as MimePart;
use Zend\Mime\PartFactory as MimePartFactory;
use Zend\Mime\Message as MimeMessage;
use Zend\Mime\MessageFactory as MimeMessageFactory;

class TransportBuilder extends \Magento\Framework\Mail\Template\TransportBuilder
{
    /** @var MimePart[] */
    private $parts = [];

    /** @var MimeMessageFactory */
    private $mimeMessageFactory;

    /** @var MimePartFactory */
    private $mimePartFactory;

    public function __construct(
        FactoryInterface $templateFactory,
        MessageInterface $message,
        SenderResolverInterface $senderResolver,
        ObjectManagerInterface $objectManager,
        TransportInterfaceFactory $mailTransportFactory,
        MimePartFactory $mimePartFactory,
        MimeMessageFactory $mimeMessageFactory,
        MessageInterfaceFactory $messageFactory = null
    ) {
        parent::__construct(
            $templateFactory,
            $message,
            $senderResolver,
            $objectManager,
            $mailTransportFactory,
            $messageFactory
        );

        $this->mimePartFactory    = $mimePartFactory;
        $this->mimeMessageFactory = $mimeMessageFactory;
    }

    protected function prepareMessage()
    {
        parent::prepareMessage();

        $mimeMessage = $this->getMimeMessage($this->message);

        foreach ($this->parts as $part) {
            $mimeMessage->addPart($part);
        }

        $this->message->setBody($mimeMessage);

        return $this;
    }

    public function addAttachment(
        $body,
        $mimeType = Mime::TYPE_OCTETSTREAM,
        $disposition = Mime::DISPOSITION_ATTACHMENT,
        $encoding = Mime::ENCODING_BASE64,
        $filename = null
    ) {
        $this->parts[] = $this->createMimePart($body, $mimeType, $disposition, $encoding, $filename);
        return $this;
    }

    private function createMimePart(
        $content,
        $type = Mime::TYPE_OCTETSTREAM,
        $disposition = Mime::DISPOSITION_ATTACHMENT,
        $encoding = Mime::ENCODING_BASE64,
        $filename = null
    ) {
        /** @var MimePart $mimePart */
        $mimePart = $this->mimePartFactory->create(['content' => $content]);
        $mimePart->setType($type);
        $mimePart->setDisposition($disposition);
        $mimePart->setEncoding($encoding);

        if ($filename) {
            $mimePart->setFileName($filename);
        }

        return $mimePart;
    }

    private function getMimeMessage(MessageInterface $message)
    {
        $body = $message->getBody();

        if ($body instanceof MimeMessage) {
            return $body;
        }

        /** @var MimeMessage $mimeMessage */
        $mimeMessage = $this->mimeMessageFactory->create();

        if ($body) {
            $mimePart = $this->createMimePart((string)$body, Mime::TYPE_TEXT, Mime::DISPOSITION_INLINE);
            $mimeMessage->setParts([$mimePart]);
        }

        return $mimeMessage;
    }
}

Não se esqueça de substituir o original \Magento\Framework\Mail\Template\TransportBuilderpela sua implementação via di.xml.

Observe que esta implementação provavelmente será interrompida com uma versão futura do Magento, que \Magento\Framework\Mail\MessageInterface::setBody()está obsoleta e pode ser removida em breve.

HTH


Oi! Você tem um método addAttachment no seu código, mas onde você os chamou? Eu não vejo isso.
Nikolai Silin

obrigado! Adicionei loop ao método prepareMessage e tudo funciona.
Nikolai Silin

@NikolaiSilin como enviar um png ou outros arquivos.
Sumeet bajaj

1

Magento 2 Email personalizado do módulo, Não fornece anexo de imagem.

Se você quiser usar o anexo de imagem com modelos de email no Magento 2, precisará substituir a classe Magento \ Framework \ Mail \ Template \ TransportBuilder

O Magento Out-of-box não fornece recurso de anexo para email. Você pode consultar blogs para enviar anexo de imagem em detalhes,

Você precisa adicionar lógica como abaixo,

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

1
Você pode ajudar a conseguir o mesmo no magento 2.3?
Sameer Bhayani

Criou anexos dessa maneira até 2.2.7. 2.2.8 e 2.3 + não estão funcionando
Matthias Kleine

Acabei de publicar uma resposta para 2.3.x @MatthiasKleine
domdambrogia

Olá, como posso anexar se tenho uma string de codificação base64?
Ketan Borada 24/09/19

1

Aqui está a resposta perfeita para enviar pdf em E-mail em magetno 2.3

$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();

Oi, ele está jogando um erro fatal: Erro Uncaught: chamada para uma função membro generateMessage () no nulo
gajjala Sandeep

Você está criando uma nova mensagem que é desnecessária quando seu transporte já tem uma mensagem. Por que não adicionar uma peça à que está no lugar? Isso é confuso e difícil de seguir. Sem mencionar que você está dobrando o trabalho e a memória necessários para resolver esse problema.
domdambrogia 20/09/19

0

Como mencionado nas respostas anteriores, o magento2 não possui uma função pronta para enviar e-mails com anexos.

Não sei se é uma prática recomendada, mas você poderia chamar diretamente a Zend_Mailclasse para fazê-lo, sem criar uma função personalizada e substituir Magento\Framework\Mail\Template\TransportBuilder, como abaixo

$mail = new \Zend_Mail('utf-8');
$mail->setFrom($senderEmail);
$mail->addTo($receiverEmail);
$mail->setSubject($subject);
$mail->setBodyHtml($text);

$content = file_get_contents($attachmentAbsolutePath);

$attachment = new \Zend_Mime_Part($content);
$attachment->type = 'text/xml'; // attachment's mime type
$attachment->disposition = \Zend_Mime::DISPOSITION_ATTACHMENT;
$attachment->encoding = \Zend_Mime::ENCODING_BASE64;
$attachment->filename = $filename;
$mail->addAttachment($attachment);
$mail->send();

antes de dar -1, é sugerido por modo de usar este comentário textarea, então todo mundo poderia entender o que está errado, thx
LucScu

$ transport-> getMessage () -> setBody ($ bodyPart);
22719 imtiazau

recebendo este erro não detectado: Chame para o método indefinido Magento \\ Framework \\ Mail \\ EmailMessage :: setBody ()
imtiazau

Estes comentários não estão relacionados com a resposta
LucScu

Estou recebendo este erro no magento 2.3.3
imtiazau 22/11/19

0

Compatível com Magento 2.3.x:

Esta foi a minha resposta para o Magento 2.3, já que essa era uma pergunta importante no google e parece haver muita gente nos comentários procurando.

Em outros posts, parece haver muita vontade de sobrescrever a TransportBuilderclasse padrão etc/di.xml, no entanto, o módulo no qual estou trabalhando é tão pequeno que não quero que seja responsável pelo padrãoTransportBuilder então criei uma classe Helper (deveria provavelmente seja um modelo baseado em como está acoplado ao modelo de email declarado - mas discordo).

O TransportBuildernão tem acesso público ao TransportInterface, mas gera um clone toda vez e, em seguida, redefine o construtor. Achei mais fácil criar minha TransportInterfaceinstância e anexar meus Partobjetos de anexo à mensagem do transporte. Se você achar necessário sobrescrever o padrão TransportBuildervia preferência de injeção de dependência, tenha cuidado ao atualizar métodos públicos. Lembre-se de praticar o O ao manter seu código SOLID !

namespace Vendor\Module\Helper;

use Magento\Framework\App\Area;
use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Helper\Context;
use Magento\Framework\DataObject;
use Magento\Framework\Filesystem\Io\File;
use Magento\Framework\Mail\Template\TransportBuilder;
use Magento\Framework\Mail\TransportInterface;
use Magento\Store\Model\StoreManagerInterface;
use Zend_Mime;
use Zend\Mime\Part;

/**
 * This was initially built out to send a single email. Abstract this as you 
 * wish.
 *
 * @package Vendor\Module\Helper
 */
class Mail extends AbstractHelper
{
    /**
     * @var Context
     */
    protected $context;

    /**
     * @var TransportBuilder
     */
    protected $transportBuilder;

    /**
     * @var StoreManagerInterface
     */
    protected $storeManager;

    /**
     * @var Config
     */
    protected $config;

    /**
     * Mail constructor.
     *
     * @param Context $context
     * @param TransportBuilder $transportBuilder
     * @param StoreManagerInterface $storeManager
     * @param Config $config
     * @param File $file
     */
    public function __construct(
        Context $context,
        TransportBuilder $transportBuilder,
        StoreManagerInterface $storeManager,
        Config $config,
        File $file
    ) {
        parent::__construct($context);
        $this->transportBuilder = $transportBuilder;
        $this->storeManager = $storeManager;
        $this->config = $config;
        $this->file = $file;
    }

    /**
     * Send the email for a Help Center submission.
     *
     * @param DataObject $templateParams
     * @param array $attachments
     * @return void
     */
    public function send(DataObject $templateParams, array $attachments = [])
    {
        $storeId = $this->storeManager->getStore()->getId();

        // Build transport
        /** @var \Magento\Framework\Mail\TransportInterface $transport */
        $transport = $this->transportBuilder
            ->setTemplateOptions(['area' => Area::AREA_FRONTEND, 'store' => $storeId])
            ->setTemplateIdentifier($this->config->getEmailTemplate())
            ->setTemplateVars($templateParams->toArray())
            ->setFrom($this->config->getEmailSender())
            ->addTo($this->config->getEmailRecipient(), 'Help Center')
            /**
             * Something important to note is that when the getTransport()
             * function is run, the message is compiled and then the builder 
             * class resets (as of 2.3.1). 
             * 
             * This is note worthy because if you want to send > 1 attachment,
             * your $builder will be reset -- losing all of the ->set* functions
             * you just used above as well as your attachment.
             * 
             * Since we append attachments to the transport, it's easier to:
             * build -> attach -> send. And this way multiple attachments 
             * can be included. :thumbsup:
             */
            ->getTransport();

        // Attach Images to transport
        foreach ($attachments as $a) {
            $transport = $this->addAttachment($transport, $a);
        }

        // Send transport
        $transport->sendMessage();
    }

    /**
     * Add an attachment to the message inside the transport builder.
     *
     * @param TransportInterface $transportBuilder
     * @param array $file Sanitized index from $_FILES
     * @return TransportInterface
     */
    protected function addAttachment(TransportInterface $transport, array $file): TransportInterface
    {
        $part = $this->createAttachment($file);
        $transport->getMessage()->addPart($part);

        return $transport;
    }

    /**
     * Create an zend mime part that is an attachment to attach to the email.
     * 
     * This was my usecase, you'll need to edit this to your own needs.
     *
     * @param array $file Sanitized index from $_FILES
     * @return Part
     */
    protected function createAttachment(array $file): Part
    {
        $ext =  '.' . explode('/', $file['type'])[1];
        $fileName = md5(uniqid(microtime()), true) . $ext;

        $attachment = new Part($this->file->read($file['tmp_name']));
        $attachment->disposition = Zend_Mime::TYPE_OCTETSTREAM;
        $attachment->encoding = Zend_Mime::ENCODING_BASE64;
        $attachment->filename = $fileName;

        return $attachment;
    }
}

Não consigo fazê-lo funcionar corretamente, sempre recebo uma exceção dizendo "Erro não capturado: Chame uma função de membro addPart () na string" ... alguma idéia sobre isso? : /
hallleron 6/11/19

1
@hallleron Curiosamente, isso é diferente do que eu estava recebendo, mas parece que você está correto. A MessageInterface::getBodyassinatura do método mostra um tipo de retorno de sequência. Você pode ter que procurar em seu TransportInterfaceobjeto, mas posso dizer que o addPartmétodo existe em um Zend\Mime\Messageobjeto. Desde magento provável estendeu essa classe para sua própria Messageclasse, eu acho que seria inteligente para tentar$transport->getMessage()->addpart($part);
domdambrogia
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.