Search code examples
eventsshopware6email-templates

MailBeforeValidateEvent in Shopware6


I'm currently working with the MailBeforeValidateEvent in Shopware and I'd like to know if this event is triggered before emails are sent to the queue. I couldn't find detailed information in the documentation, and I'm hoping someone with experience in this area could clarify.


Solution

  • Assuming you are using the newest Shopware 6 version (currently: v6.5.6.1) in a default configuration:

    The MailBeforeValidateEvent is dispatched at the start of Shopware\Core\Content\Mail\Service\MailService::send():

    /**
     * @param mixed[] $data
     * @param mixed[] $templateData
     */
    public function send(array $data, Context $context, array $templateData = []): ?Email
    {
        $event = new MailBeforeValidateEvent($data, $context, $templateData);
        $this->eventDispatcher->dispatch($event);
        /* ... */
    

    Then — towards the end of the same function — the mail is sent:

        /* ... */
        $this->mailSender->send($mail);
    
        $event = new MailSentEvent($data['subject'], $recipients, $contents, $context, $templateData['eventName'] ?? null);
        $this->eventDispatcher->dispatch($event);
    
        return $mail;
    }
    

    Which leads to Shopware\Core\Content\Mail\Service\MailSender::send() which calls Symfony\Component\Mailer\Mailer::send(), inside the Symfony\Component\Mailer\Messenger\SendEmailMessage is dispatched:

    public function send(RawMessage $message, Envelope $envelope = null): void
    {
        if (null === $this->bus) {
            $this->transport->send($message, $envelope);
    
            return;
        }
    
        $stamps = [];
        if (null !== $this->dispatcher) {
            // The dispatched event here has `queued` set to `true`; the goal is NOT to render the message, but to let
            // listeners do something before a message is sent to the queue.
            // We are using a cloned message as we still want to dispatch the **original** message, not the one modified by listeners.
            // That's because the listeners will run again when the email is sent via Messenger by the transport (see `AbstractTransport`).
            // Listeners should act depending on the `$queued` argument of the `MessageEvent` instance.
            $clonedMessage = clone $message;
            $clonedEnvelope = null !== $envelope ? clone $envelope : Envelope::create($clonedMessage);
            $event = new MessageEvent($clonedMessage, $clonedEnvelope, (string) $this->transport, true);
            $this->dispatcher->dispatch($event);
            $stamps = $event->getStamps();
    
            if ($event->isRejected()) {
                return;
            }
        }
    
        try {
            $this->bus->dispatch(new SendEmailMessage($message, $envelope), $stamps);
        } catch (HandlerFailedException $e) {
            foreach ($e->getNestedExceptions() as $nested) {
                if ($nested instanceof TransportExceptionInterface) {
                    throw $nested;
                }
            }
            throw $e;
        }
    }
    

    It gets handled by the Symfony\Component\Mailer\Messenger\MessageHandler, which calls the configured mail transport (sendmail, etc.).

    So MailBeforeValidateEvent is triggered before the email is sent to the queue.