Magento 2: envíe un correo electrónico con el adjunto

Respuestas:

19

M2 no viene listo para usar, sin embargo, es una característica integrada en el marco zend. Aquí hay una buena referencia sobre cómo agregar esta funcionalidad a magento: https://blog.bitexpert.de/blog/sending-mails-with-attachments-in-magento-2/

En caso de que el enlace se bloquee, cree lo siguiente

<?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;
    }
}

luego agregue 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>

Ahora puede usarlo en addAttachment()todo su sitio.

Xenocide8998
fuente
8
Todavía me pregunto por qué magento TransportBuilder no tiene este método
Murtuza Zabuawala
44
¿Cómo podemos adjuntar un archivo en el correo electrónico personalizado magento 2.3? porque está usando zendframework 2 y esta respuesta ya no funciona
Manish Maheshwari
3
¿Cómo enviar correos electrónicos con archivos adjuntos en Magento 2.3?
Dhaduk Mitesh
@ManishMaheshwari & Mitesh ¿Tienes la solución?
Sameer Bhayani
1
Esta solución ya no funciona en Magento2.3. ¿Alguien tiene una alternativa para el apego?
nishu
8

A partir de Magento 2.2.7, las soluciones descritas anteriormente ya no funcionan ya que \Magento\Framework\Mail\Message dejó de extenderse \Zend_Mail.
Para evitar la falta de una manera fácil de agregar archivos adjuntos a través del constructor de transporte (que actualmente parece ser el lugar correcto para tal función), es necesario crear un reemplazo para el TransportBuilder y utilizar \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;
    }
}

No olvide reemplazar el original \Magento\Framework\Mail\Template\TransportBuilderpor su implementación a través dedi.xml .

Tenga en cuenta que esta implementación probablemente se romperá con un próximo lanzamiento de Magento como \Magento\Framework\Mail\MessageInterface::setBody() está en desuso y puede eliminarse pronto.

HTH

Jean-Bernard Valentaten
fuente
¡Hola! Tiene un método addAttachment en su código, pero ¿dónde los llamó? No lo veo
Nikolai Silin
¡Gracias! Agregué un bucle para preparar el método de mensaje y todo funciona.
Nikolai Silin
@NikolaiSilin cómo enviar un png u otros archivos.
sumeet bajaj
1

Correo electrónico personalizado de Magento 2 desde el módulo, no proporciona datos adjuntos

Si desea utilizar archivos adjuntos de imágenes con plantillas de correo electrónico en Magento 2, debe anular la clase, Magento \ Framework \ Mail \ Template \ TransportBuilder

Magento listo para usar no proporciona la función de archivo adjunto para correo electrónico. Puede referir blogs para enviar datos adjuntos de imágenes en detalles,

Necesitas agregar lógica como a continuación,

 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;
    }
Rakesh Jesadiya
fuente
1
¿Puedes ayudar a lograr lo mismo en magento 2.3?
Sameer Bhayani
Archivos adjuntos creados de esta manera hasta 2.2.7. 2.2.8 y 2.3+ no funcionan
Matthias Kleine
Acabo de publicar una respuesta para 2.3.x @MatthiasKleine
domdambrogia
hola, ¿cómo puedo adjuntar si tengo una cadena de codificación base64?
Ketan Borada
1

Aquí hay una respuesta perfecta para enviar pdf en correo electrónico en 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();
jignesh patel
fuente
Hola, arroja un error fatal: Error no detectado: Llamada a una función miembro generateMessage () en nulo
gajjala sandeep
Está creando un nuevo mensaje que es innecesario cuando su transporte ya tiene un mensaje. ¿Por qué no simplemente agregar una parte a la que está en su lugar? Esto es desordenado y difícil de seguir. Sin mencionar que está duplicando el trabajo y la memoria necesarios para resolver este problema.
domdambrogia
1

Compatible con Magento 2.3.x:

Esta fue mi respuesta para Magento 2.3 ya que esta era una pregunta importante en Google y parece que hay mucha gente mirando los comentarios.

Parece que hay muchas ganas en otras publicaciones sobre sobrescribir la TransportBuilderclase predeterminada a través de etc/di.xml, sin embargo, el módulo en el que estoy trabajando es tan pequeño que no quiero que sea responsable de la clase predeterminadaTransportBuilder así que construí una clase auxiliar (debería probablemente sea un modelo basado en qué tan acoplado está a la plantilla de correo electrónico declarada, pero estoy divagando).

El TransportBuilderno tiene acceso público al TransportInterface, sino que genera un clon cada vez y luego restablece el generador. Me resultó más fácil construir mi TransportInterfaceinstancia y luego adjuntar mis Partobjetos adjuntos al mensaje del transporte. Si considera necesario sobrescribir el valor predeterminado TransportBuildermediante la preferencia de inyección de dependencia, tenga cuidado al actualizar los métodos públicos. ¡Recuerde practicar el O cuando mantenga su código SOLIDO !

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;
    }
}
domdambrogia
fuente
No puedo hacer que funcione correctamente, siempre recibo una excepción que dice "Error no detectado: Llamar a una función miembro addPart () en la cadena" ... ¿alguna idea sobre eso? : /
hallleron
1
@hallleron Curiosamente, esto es diferente de lo que estaba obteniendo, pero parece que estás en lo correcto. La MessageInterface::getBodyfirma del método muestra un tipo de retorno de cadena. Puede que tenga que cavar en su TransportInterfaceobjeto, pero puedo decirle que el addPartmétodo existe en un Zend\Mime\Messageobjeto. Dado que magento probablemente extendió esa clase para su propia Messageclase, creo que sería inteligente intentarlo$transport->getMessage()->addpart($part);
domdambrogia
0

Como se mencionó en las respuestas anteriores, magento2 no tiene una función lista para usar para enviar correos con archivos adjuntos.

No sé si es una mejor práctica, pero podría llamar directamente a la Zend_Mailclase para hacerlo, sin crear una función personalizada y anular Magento\Framework\Mail\Template\TransportBuilder, como se muestra a continuación

$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();
LucScu
fuente
antes de dar -1, se sugiere usar este comentario textarea, entonces todos podrían entender lo que está mal, gracias
LucScu
$ transporte-> getMessage () -> setBody ($ bodyPart);
imtiazau
obteniendo este Error no detectado: Llame al método indefinido Magento \\ Framework \\ Mail \\ EmailMessage :: setBody ()
imtiazau
Estos comentarios no están relacionados con la respuesta
LucScu
recibo este error en magento 2.3.3
imtiazau