Magento 2: wyślij e-mail z załącznikiem

Odpowiedzi:

19

M2 nie wchodzi w skład zestawu, ale jest to funkcja wbudowana w środowisko Zend. Oto dobre odniesienie, jak dodać tę funkcjonalność do magento: https://blog.bitexpert.de/blog/sending-mails-with-attachments-in-magento-2/

Jeśli link przestanie działać, utwórz następujące

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

następnie dodaj do 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>

Teraz możesz korzystać z addAttachment()całej witryny.

Ksenocyd 8998
źródło
8
Nadal zastanawiam się, dlaczego magento TransportBuilder nie ma tej metody
Murtuza Zabuawala
4
Jak możemy załączyć plik w niestandardowym e-mailu Magento 2.3? ponieważ używa zendframework 2 i ta odpowiedź już nie działa
Manish Maheshwari
3
Jak wysłać wiadomość e-mail z załącznikiem w Magento 2.3?
Dhaduk Mitesh
@ManishMaheshwari & Mitesh Masz rozwiązanie?
Sameer Bhayani,
1
To rozwiązanie nie działa już w Magento2.3. Czy ktoś ma alternatywę dla przywiązania?
nishu
8

Począwszy od Magento 2.2.7 opisane powyżej rozwiązania już nie działają, ponieważ \Magento\Framework\Mail\Messageporzucono rozszerzenie \Zend_Mail.
Aby obejść brak łatwego sposobu dodawania załączników za pomocą konstruktora transportu (który obecnie wydaje się właściwym miejscem dla takiej funkcji), należy utworzyć zamiennik TransportBuilder i skorzystać z \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;
    }
}

Nie zapomnij zastąpić oryginału \Magento\Framework\Mail\Template\TransportBuilderswoją implementacją za pośrednictwem di.xml.

Pamiętaj, że ta implementacja prawdopodobnie \Magento\Framework\Mail\MessageInterface::setBody()zepsuje się wraz z nadchodzącą wersją Magento, ponieważ jest przestarzała i może zostać wkrótce usunięta.

HTH

Jean-Bernard Valentaten
źródło
Cześć! Masz kod addAttachment w kodzie, ale gdzie je wywołałeś? Nie widzę tego
Nikolai Silin
dzięki! Dodałem pętlę do metody przygotowywania wiadomości i wiecznych prac.
Nikolai Silin
@NikolaiSilin jak wysłać png lub inne pliki.
sumeet bajaj
1

Magento 2 Niestandardowy e-mail z modułu, nie zawiera załącznika do obrazu.

Jeśli chcesz używać załącznika obrazu z szablonami e-mail w Magento 2, musisz zastąpić klasę, Magento \ Framework \ Mail \ Template \ TransportBuilder

Magento Out-of-box nie zapewnia funkcji załączników do wiadomości e-mail. Możesz odsyłać blogi w celu przesłania załącznika obrazu szczegółowo,

Musisz dodać logikę jak poniżej,

 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
źródło
1
Czy możesz pomóc osiągnąć to samo w Magento 2.3?
Sameer Bhayani
Utworzono załączniki w ten sposób do 2.2.7. 2.2.8 i 2.3+ nie działają
Matthias Kleine
Właśnie opublikowałem odpowiedź na 2.3.x @MatthiasKleine
domdambrogia,
cześć, jak mogę dołączyć, jeśli mam ciąg kodowania base64?
Ketan Borada
1

Oto idealna odpowiedź na przesłanie pdf w e-mailu w 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
źródło
Cześć,
Zgłasza
Tworzysz nową wiadomość, która nie jest potrzebna, gdy Twój transport ma już wiadomość. Dlaczego nie po prostu dodać części do tej na miejscu? Jest to bałagan i trudne do naśladowania. Nie wspominając o tym, że podwajasz pracę i pamięć potrzebną do rozwiązania tego problemu.
domdambrogia,
1

Kompatybilny z Magento 2.3.x:

To była moja odpowiedź dla Magento 2.3, ponieważ było to najważniejsze pytanie w Google i wydaje się, że wiele osób szuka komentarzy.

Wygląda na to, że w innych postach jest wiele chęci zastąpienia domyślnej TransportBuilderklasy poprzezetc/di.xml , jednak moduł, nad którym pracuję, jest tak mały, że nie chcę, aby był odpowiedzialny za domyślny, TransportBuilderwięc zbudowałem klasę Pomocnika (powinien prawdopodobnie będzie modelem opartym na tym, jak jest powiązany z zadeklarowanym szablonem e-maila - ale przechodzę do dygresji).

TransportBuilderNie ma publicznego dostępu do TransportInterface, lecz za każdym razem generuje klon, a następnie resetuje Builder. Łatwiej było mi zbudować TransportInterfaceinstancję, a następnie dołączyć Partobiekty załącznika do komunikatu transportu. Jeśli uznasz, że konieczne jest zastąpienie ustawień domyślnych TransportBuilderza pomocą preferencji wstrzykiwania zależności, należy zachować ostrożność przy aktualizacji metod publicznych. Pamiętaj, aby ćwiczyć O , zachowując kod 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;
    }
}
domdambrogia
źródło
Nie mogę sprawić, aby działał poprawnie, zawsze pojawia się wyjątek z informacją: „Nieprzechwycony błąd: wywołanie funkcji członkowskiej addPart () w ciągu znaków” ... jakiś pomysł na ten temat? : /
hallleron
1
@hallleron Dziwne, że to różni się od tego, co otrzymywałem, ale wygląda na to, że masz rację. MessageInterface::getBodyPodpis metoda pokazuje typ zwracany ciąg. Być może będziesz musiał przekopać się w swoim TransportInterfaceobiekcie, ale mogę ci powiedzieć, że addPartmetoda istnieje na Zend\Mime\Messageobiekcie. Ponieważ magento prawdopodobnie rozszerzyło tę klasę na własną Messageklasę, myślę, że byłoby mądre spróbować$transport->getMessage()->addpart($part);
domdambrogia
0

Jak wspomniano w poprzednich odpowiedziach, magento2 nie ma gotowej funkcji do wysyłania wiadomości e-mail z załącznikami.

Nie wiem, czy jest to najlepsza praktyka, ale możesz wywołać bezpośrednio Zend_Mailklasę, aby to zrobić, bez tworzenia niestandardowej funkcji i zastępowania Magento\Framework\Mail\Template\TransportBuilder, jak poniżej

$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
źródło
przed podaniem -1, zaleca się, aby użyć tego komentarza w polu tekstowym, aby każdy mógł zrozumieć, co jest nie tak, dzięki
LucScu
$ transport-> getMessage () -> setBody ($ bodyPart);
imtiazau
uzyskiwanie tego nieprzechwyconego błędu: Wywołanie niezdefiniowanej metody Magento \\ Framework \\ Mail \\ EmailMessage :: setBody ()
imtiazau
Te komentarze nie są związane z odpowiedzią
LucScu,
dostaję ten błąd w Magento 2.3.3
imtiazau