Question

I have created one extension which is send mail on particular event. This extension working fine in Magento 2.3.2 and also send email but this extension not working in Magento 2.3.3. I have read Magento 2.3.3 release note and I found that Email class is changed in Magento 2.3.3. Read here Email Library

Can anyone help me how can I make send mail code compatible for both version. here is my code.

<?php

namespace Vendor\Modulename\Observer;

use Magento\Framework\Event\ObserverInterface;
use \Magento\Store\Model\StoreManagerInterface;
use \Magento\Framework\Mail\Template\TransportBuilder;
use \Magento\Framework\Translate\Inline\StateInterface;
use Psr\Log\LoggerInterface;

class SendMail implements ObserverInterface
{
    protected $scopeConfig;

    const XML_PATH_TEMPLATE_SUBJECT = 'mycustomdemo/general/template_subject';
    const XML_PATH_SENDER_NAME = 'mycustomdemo/general/sender_name';
    const XML_PATH_SENDER_EMAIL = 'mycustomdemo/general/sender_email';
    const XML_PATH_SENDER_MESSAGE = 'mycustomdemo/general/sender_message';

    public function __construct(
        StoreManagerInterface $storeManager,
        \Magento\Framework\Message\ManagerInterface $messageManager,
        TransportBuilder $transportBuilder,
        LoggerInterface $logLoggerInterface,
        StateInterface $inlineTranslation,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
    ) {
        $this->storeManager = $storeManager;
        $this->messageManager = $messageManager;
        $this->transportBuilder = $transportBuilder;
        $this->inlineTranslation = $inlineTranslation;
        $this->logLoggerInterface = $logLoggerInterface;
        $this->scopeConfig = $scopeConfig;
    }
    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        try {
                $storeScope = \Magento\Store\Model\ScopeInterface::SCOPE_STORE;
                $templateSubject = $this->scopeConfig->getValue(self::XML_PATH_TEMPLATE_SUBJECT, $storeScope);
                $senderName = $this->scopeConfig->getValue(self::XML_PATH_SENDER_NAME, $storeScope);
                $senderEmail = $this->scopeConfig->getValue(self::XML_PATH_SENDER_EMAIL, $storeScope);
                $senderMessage = $this->scopeConfig->getValue(self::XML_PATH_SENDER_MESSAGE, $storeScope);
                $order = $observer->getEvent()->getOrder();
                $custFirstname = $order->getCustomerEmail();
                $custFirstname = $order->getCustomerFirstname();
                $custEmail = $order->getCustomerEmail();
                $template_sub = $templateSubject; # Set email subject
                $sender_name = $senderName; # Set sender name
                $sender_email = $senderEmail; # Set sender email
                $template_content = $senderMessage; # Set email content
                $templateId = 'cancel_order_template'; #This is my custom template ID
                $this->inlineTranslation->suspend();
                $storeScope = \Magento\Store\Model\ScopeInterface::SCOPE_STORE;
                $sendFrom = $sender_email;
                $senderName = $sender_name;
                $sendTo = $custEmail;
                $transport = $this->transportBuilder
                ->setTemplateIdentifier($templateId)
                ->setTemplateOptions(
                    [
                        'area' => 'frontend',
                        'store' => $this->storeManager->getStore()->getId(),
                    ]
                )
                ->setTemplateVars([
                    'template_subject' => $template_sub,
                    'email_content' => $template_content,
                    'order'=> $order
                ])
                ->setFrom(['email' => $sendFrom, 'name' => $senderName])
                ->addTo([$sendTo])
                ->getTransport();
                $transport->sendMessage();
                $this->inlineTranslation->resume();
        } catch (\Exception $e) {
            $this->logLoggerInterface->debug($e->getMessage());
        }
    }
}

I am facing this error in debug.log -- Magento 2.3.3

[2019-11-23 08:45:32] main.CRITICAL: Type Error occurred when creating object: Magento\Framework\Mail\EmailMessage, Argument 2 passed to Magento\Framework\Mail\EmailMessage::__construct() must be of the type array, null given, called in /opt/lampp72/htdocs/magento2/vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php on line 116 [] []
[2019-11-23 08:45:32] main.DEBUG: Type Error occurred when creating object: Magento\Framework\Mail\EmailMessage [] []

Any help would be appreciated.

Thanks in advance..!

Was it helpful?

Solution

You can remove array from addTo([$sendTo]) function. If you'll remove that and add only single email address there then it will work.

<?php

namespace Vendor\Modulename\Observer;

use Magento\Framework\Event\ObserverInterface;
use \Magento\Store\Model\StoreManagerInterface;
use \Magento\Framework\Mail\Template\TransportBuilder;
use \Magento\Framework\Translate\Inline\StateInterface;
use Psr\Log\LoggerInterface;

class SendMail implements ObserverInterface
{
    protected $scopeConfig;

    const XML_PATH_TEMPLATE_SUBJECT = 'mycustomdemo/general/template_subject';
    const XML_PATH_SENDER_NAME = 'mycustomdemo/general/sender_name';
    const XML_PATH_SENDER_EMAIL = 'mycustomdemo/general/sender_email';
    const XML_PATH_SENDER_MESSAGE = 'mycustomdemo/general/sender_message';

    public function __construct(
        StoreManagerInterface $storeManager,
        \Magento\Framework\Message\ManagerInterface $messageManager,
        TransportBuilder $transportBuilder,
        LoggerInterface $logLoggerInterface,
        StateInterface $inlineTranslation,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
    ) {
        $this->storeManager = $storeManager;
        $this->messageManager = $messageManager;
        $this->transportBuilder = $transportBuilder;
        $this->inlineTranslation = $inlineTranslation;
        $this->logLoggerInterface = $logLoggerInterface;
        $this->scopeConfig = $scopeConfig;
    }
    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        try {
                $storeScope = \Magento\Store\Model\ScopeInterface::SCOPE_STORE;
                $templateSubject = $this->scopeConfig->getValue(self::XML_PATH_TEMPLATE_SUBJECT, $storeScope);
                $senderName = $this->scopeConfig->getValue(self::XML_PATH_SENDER_NAME, $storeScope);
                $senderEmail = $this->scopeConfig->getValue(self::XML_PATH_SENDER_EMAIL, $storeScope);
                $senderMessage = $this->scopeConfig->getValue(self::XML_PATH_SENDER_MESSAGE, $storeScope);
                $order = $observer->getEvent()->getOrder();
                $custFirstname = $order->getCustomerEmail();
                $custFirstname = $order->getCustomerFirstname();
                $custEmail = $order->getCustomerEmail();
                $template_sub = $templateSubject; # Set email subject
                $sender_name = $senderName; # Set sender name
                $sender_email = $senderEmail; # Set sender email
                $template_content = $senderMessage; # Set email content
                $templateId = 'cancel_order_template'; #This is my custom template ID
                $this->inlineTranslation->suspend();
                $storeScope = \Magento\Store\Model\ScopeInterface::SCOPE_STORE;
                $sendFrom = $sender_email;
                $senderName = $sender_name;
                $sendTo = $custEmail;
                $transport = $this->transportBuilder
                ->setTemplateIdentifier($templateId)
                ->setTemplateOptions(
                    [
                        'area' => 'frontend',
                        'store' => $this->storeManager->getStore()->getId(),
                    ]
                )
                ->setTemplateVars([
                    'template_subject' => $template_sub,
                    'email_content' => $template_content,
                    'order'=> $order
                ])
                ->setFrom(['email' => $sendFrom, 'name' => $senderName])
                ->addTo($sendTo) //I make change here and it's working now.
                ->getTransport();
                $transport->sendMessage();
                $this->inlineTranslation->resume();
        } catch (\Exception $e) {
            $this->logLoggerInterface->debug($e->getMessage());
        }
    }
}

Because there is one if condition where isset function is used and whenever we will use addTo() function with string type of argument at that time that is working because first condition become true there. But when we will add array as an argument in that function at that time when we check $this->messageData there is not any index with to addressType. So I just initialize that index at starting of this function and assign blank array as an argument. So now whenever we will use array as an argument second argument become true in that case and we can send multiple emails.

Hope this will help you!

OTHER TIPS

Please try following code.

Add app/code/vendor/module/etc/di.xml Add following code

<?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\TransportBuilder" />
</config>

Now add app/code/vendor/module/model/mail/template/TransportBuilder.php and add following code

    <?php
declare(strict_types=1);

namespace Vendor\Module\Model\Mail\Template;

use Magento\Framework\App\TemplateTypesInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\MailException;
use Magento\Framework\Mail\EmailMessageInterface;
use Magento\Framework\Mail\EmailMessageInterfaceFactory;
use Magento\Framework\Mail\AddressConverter;
use Magento\Framework\Mail\Exception\InvalidArgumentException;
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\TemplateInterface;
use Magento\Framework\Mail\TransportInterface;
use Magento\Framework\Mail\TransportInterfaceFactory;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Phrase;
use Zend\Mime\Mime;
use Zend\Mime\PartFactory;

/**
 * TransportBuilder
 *
 * @api
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
 * @since 100.0.2
 */
class TransportBuilder extends \Magento\Framework\Mail\Template\TransportBuilder
{
    /**
     * Template Identifier
     *
     * @var string
     */
    protected $templateIdentifier;

    /**
     * Template Model
     *
     * @var string
     */
    protected $templateModel;

    /**
     * Template Variables
     *
     * @var array
     */
    protected $templateVars;

    /**
     * Template Options
     *
     * @var array
     */
    protected $templateOptions;

    /**
     * Mail Transport
     *
     * @var TransportInterface
     */
    protected $transport;

    /**
     * Template Factory
     *
     * @var FactoryInterface
     */
    protected $templateFactory;

    /**
     * Object Manager
     *
     * @var ObjectManagerInterface
     */
    protected $objectManager;

    /**
     * Message
     *
     * @var EmailMessageInterface
     */
    protected $message;

    /**
     * Sender resolver
     *
     * @var SenderResolverInterface
     */
    protected $_senderResolver;

    /**
     * @var TransportInterfaceFactory
     */
    protected $mailTransportFactory;

    /**
     * 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|null
     */
    private $addressConverter;

    protected $attachments = [];

    protected $partFactory;

    /**
     * TransportBuilder constructor
     *
     * @param FactoryInterface $templateFactory
     * @param MessageInterface $message
     * @param SenderResolverInterface $senderResolver
     * @param ObjectManagerInterface $objectManager
     * @param TransportInterfaceFactory $mailTransportFactory
     * @param MessageInterfaceFactory|null $messageFactory
     * @param EmailMessageInterfaceFactory|null $emailMessageInterfaceFactory
     * @param MimeMessageInterfaceFactory|null $mimeMessageInterfaceFactory
     * @param MimePartInterfaceFactory|null $mimePartInterfaceFactory
     * @param addressConverter|null $addressConverter
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
     */
    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);
    }

    /**
     * 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;
    }

    /**
     * Set template identifier
     *
     * @param string $templateIdentifier
     *
     * @return $this
     */
    public function setTemplateIdentifier($templateIdentifier)
    {
        $this->templateIdentifier = $templateIdentifier;

        return $this;
    }

    /**
     * Set template model
     *
     * @param string $templateModel
     *
     * @return $this
     */
    public function setTemplateModel($templateModel)
    {
        $this->templateModel = $templateModel;
        return $this;
    }

    /**
     * Set template vars
     *
     * @param array $templateVars
     *
     * @return $this
     */
    public function setTemplateVars($templateVars)
    {
        $this->templateVars = $templateVars;

        return $this;
    }

    /**
     * Set template options
     *
     * @param array $templateOptions
     * @return $this
     */
    public function setTemplateOptions($templateOptions)
    {
        $this->templateOptions = $templateOptions;

        return $this;
    }

    /**
     * Get mail transport
     *
     * @return TransportInterface
     * @throws LocalizedException
     */
    public function getTransport()
    {
        try {
            $this->prepareMessage();
            $mailTransport = $this->mailTransportFactory->create(['message' => clone $this->message]);
        } finally {
            $this->reset();
        }

        return $mailTransport;
    }

    /**
     * Reset object state
     *
     * @return $this
     */
    protected function reset()
    {
        $this->messageData = [];
        $this->templateIdentifier = null;
        $this->templateVars = null;
        $this->templateOptions = null;
        return $this;
    }

    /**
     * Get template
     *
     * @return TemplateInterface
     */
    protected function getTemplate()
    {
        return $this->templateFactory->get($this->templateIdentifier, $this->templateModel)
            ->setVars($this->templateVars)
            ->setOptions($this->templateOptions);
    }

    /**
     * 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:
                $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;
    }

    /**
     * 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,$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
            );
        }
    }

    /**
     * @param string|null $content
     * @param string|null $fileName
     * @param string|null $fileType
     * @return TransportBuilder
     */
    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;
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top