Tutorial: Sending an email

Edit on GitHub
You are browsing a previous version of the document. The latest version is 202307.0.

The following example represents a real-world scenario: CustomerRegistration.

A customer goes through the registration process in your frontend (Yves) and all of the customer’s information is sent to Zed. Zed uses the provided information to register the customer. Once the registration is completed, the customer receives a confirmation email in their inbox.

1. Handle mail usage

In the model which handles registration, you can override the sendRegistrationToken function:

<?php

namespace Pyz\Zed\Customer\Business\Customer;

use Generated\Shared\Transfer\CustomerTransfer;
use Generated\Shared\Transfer\MailTransfer;
use Spryker\Zed\Customer\Business\Customer\Customer as SprykerCustomer;
use Spryker\Zed\Customer\Communication\Plugin\Mail\CustomCustomerRegistrationMailTypeBuilderPlugin;

class Customer extends SprykerCustomer
{
    /**
     * @param \Generated\Shared\Transfer\CustomerTransfer $customerTransfer
     *
     * @return bool
     */
    protected function sendRegistrationToken(CustomerTransfer $customerTransfer)
    {
        // Create a MailTransfer instance which is
        // used for further processing
        $mailTransfer = new MailTransfer();

        // Set the mail type which is used for the
        // internal mapping—for example, which mail provider
        // should send this mail
        $mailTransfer->setType(CustomCustomerRegistrationMailTypeBuilderPlugin::MAIL_TYPE);

        // Set the CustomerTransfer to the MailTransfer
        // this can be any Transfer object which is
        // needed in the Mail
        $mailTransfer->setCustomer($customerTransfer);

        // Set the LocaleTransfer which should be used
        // for, for example, translation inside your templates
        $mailTransfer->setLocale($customerTransfer->getLocale());

        // Trigger the mail facade to handle the mail
        $this->mailFacade->handleMail($mailTransfer);
    }
}

You can also override factory:

<?php

namespace Pyz\Zed\Customer\Business;

use Pyz\Zed\Customer\Business\Customer\Customer;
use Spryker\Zed\Customer\Business\Customer\CustomerInterface;
use Spryker\Zed\Customer\Business\CustomerBusinessFactory as SprykerCustomerBusinessFactory;

class CustomerBusinessFactory extends SprykerCustomerBusinessFactory
{
    /**
     * @return \Spryker\Zed\Customer\Business\Customer\CustomerInterface
     */
    public function createCustomer(): CustomerInterface
    {
        $config = $this->getConfig();

        $customer = new Customer(
            $this->getQueryContainer(),
            $this->createCustomerReferenceGenerator(),
            $config,
            $this->createEmailValidator(),
            $this->getMailFacade(),
            $this->getLocaleQueryContainer(),
            $this->getStore(),
            $this->createCustomerExpander(),
            $this->getPostCustomerRegistrationPlugins()
        );

        return $customer;
    }
}

All MailTransfers need to know which mail type, which must be a string, has to be used for further internal processing. For example:

protected function sendRegistrationToken()
{
    $mailTransfer = new MailTransfer();
    $mailTransfer->setType(YourMailTypePlugin::MAIL_TYPE);
    $this->mailFacade->handleMail($mailTransfer);
}

2. Creating a MailTypeBuilderPlugin

Create MailTypeBuilderPlugin by implementing MailTypeBuilderPluginInterface. For more information about creating a MailTypeBuilderPlugin, see HowTo: Create and register a MailTypeBuilderPlugin:

Code sample:
<?php

namespace Pyz\Zed\Customer\Communication\Plugin\Mail;

use Generated\Shared\Transfer\MailRecipientTransfer;
use Generated\Shared\Transfer\MailTemplateTransfer;
use Generated\Shared\Transfer\MailTransfer;
use Spryker\Zed\Kernel\Communication\AbstractPlugin;
use Spryker\Zed\MailExtension\Dependency\Plugin\MailTypeBuilderPluginInterface;

class CustomCustomerRegistrationMailTypeBuilderPlugin extends AbstractPlugin implements MailTypeBuilderPluginInterface
{
    protected const MAIL_TYPE = 'custom customer registration mail';

    protected const MAIL_TEMPLATE_HTML = 'customer/mail/customer_registration.html.twig';

    protected const MAIL_TEMPLATE_TEXT = 'customer/mail/customer_registration.text.twig';

    protected const GLOSSARY_KEY_MAIL_SUBJECT = 'mail.customer.registration.subject';

    public function getName(): string
    {
        return static::MAIL_TYPE;
    }

    public function build(MailTransfer $mailTransfer): MailTransfer
    {
        $customerTransfer = $mailTransfer->requireCustomer()->getCustomer();

        return $mailTransfer
            ->setSubject(static::GLOSSARY_KEY_MAIL_SUBJECT)
            ->addTemplate(
                (new MailTemplateTransfer())
                    ->setName(static::MAIL_TEMPLATE_HTML)
                    ->setIsHtml(true),
            )
            ->addTemplate(
                (new MailTemplateTransfer())
                    ->setName(static::MAIL_TEMPLATE_TEXT)
                    ->setIsHtml(false),
            )
            ->addRecipient(
                (new MailRecipientTransfer())
                    ->setEmail($customerTransfer->getEmail())
                    ->setName(sprintf('%s %s', $customerTransfer->getFirstName(), $customerTransfer->getLastName())),
            );
    }
}

3. Registering a plugin

When a plugin is created, it must be registered in MailDependencyProvider:

<?php
namespace Pyz\Zed\Mail;

use Pyz\Zed\Customer\Communication\Plugin\Mail\CustomCustomerRegistrationMailTypeBuilderPlugin;
use Spryker\Zed\Mail\MailDependencyProvider as SprykerMailDependencyProvider;

class MailDependencyProvider extends SprykerMailDependencyProvider
{
    protected function getMailTypeBuilderPlugins(): array
    {
        return [
            new CustomCustomerRegistrationMailTypeBuilderPlugin(),
        ];
    }
}

4. Mail translations

MailTypeBuilderPlugin also has access to the glossary with the setSubject() method.

A string is used as a key of the translation. The default mail provider does the translation internally through GlossaryFacade.

You can also translate with the parameters setting up the placeholder to be replaced. For the mail.order.shipped.subject key, you have Your order {orderReference} is on its way as translation. In MailTypeBuilderPlugin, you can use orderReference from the given OrderTransfer within the subject translation:

<?php
use Generated\Shared\Transfer\MailTransfer;
use Generated\Shared\Transfer\MailRecipientTransfer;
use Generated\Shared\Transfer\MailSenderTransfer;
use Spryker\Zed\Kernel\Communication\AbstractPlugin;
use Spryker\Zed\MailExtension\Dependency\Plugin\MailTypeBuilderPluginInterface;

class CustomCustomerRegistrationMailTypeBuilderPlugin extends AbstractPlugin implements MailTypeBuilderPluginInterface
{
    public function build(MailTransfer $mailTransfer): MailTransfer
    {
        return $mailTransfer
            //
            ->setSubject('Registration {customerName}')
            ->setSubjectTranslations([
                '{customerName}' => 'Spencor Hopkins'
            ])
            ->addRecipient(
                (new MailRecipientTransfer())
                    ->setName('{customerName}')
                    ->setNameTranslationParameters([
                        '{customerName}' => 'Spencor Hopkins'
                ]),
            )
            ->addSender(
                (new MailSenderTransfer())
                    ->setName('{senderName}')
                    ->setNameTranslationParameters([
                        '{senderName}' => 'Spryker'
                ]),
            );
    }
}
Info
`MailSenderTransfer.setName()`, `MailRecipientTransfer.setName()` as well as `MailTransfer.setSubject()` allow setting up translations. Besides that, `MailSenderTransfer.setNameTranslations()` and `MailRecipientTransfer.setNameTranslations()` are used in order to translate with parameters.

Set templates

You usually have a .twig file which contains the template you want to use for emails.

Set the template in MailTransfer, which must be used in the MailTypeBuilderPlugin plugin.

<?php
use Generated\Shared\Transfer\MailTransfer;
use Spryker\Zed\Kernel\Communication\AbstractPlugin;
use Spryker\Zed\MailExtension\Dependency\Plugin\MailTypeBuilderPluginInterface;

class CustomCustomerRegistrationMailTypeBuilderPlugin extends AbstractPlugin implements MailTypeBuilderPluginInterface
{
    protected const MAIL_TEMPLATE_TEXT = 'customer/mail/customer_registration.text.twig';

    public function build(MailTransfer $mailTransfer): MailTransfer
    {
        return $mailTransfer
            //
            ->addTemplate(
                (new MailTemplateTransfer())
                    ->setName(static::MAIL_TEMPLATE_TEXT)
                    ->setIsHtml(false),
            );
    }
}

The provider determines the template’s final look. It can contain information that can be stored in either plain text, or in HTML. For example, you can have a template that generates JSON:

{
    customer: "{{ mail.customer.firstName }} {{ mail.customer.lastName }}",
}

In the following example, you have a plain text template:

{{ 'mail.customer.registration.text' | trans }}

The templates must be placed within the module’s Presentation layer, such as src/Pyz/Zed/Customer/Presentation/Mail/customer_registration.text.twig. You can use the same trans filter as with Yves and Zed templates.

TwigRenderer is the default renderer, but you can add your own renderer by implementing RendererInterface.

Spryker also provides a basic layout file, where you can inject concrete content files. If you want to build your own layout, you need the following in your template:

{% for template in mail.templates %}
    {% if not template.isHtml %}
        {% include "@" ~ template.name with {mail: mail} %}
    {% endif %}
{% endfor %}

The preceeding template is used for plain text messages, and templates can also be used to generate JSON or query strings like customer={{ mail.customer.firstName }}&orderReference={{ mail.order.orderReference }}. It’s up to your provider to decide what to render.

For HTML based messages, you need to have this in your layout file:

{% for template in mail.templates %}
    {% if template.isHtml %}
        {% include "@" ~ template.name with {mail: mail} %}
    {% endif %}
{% endfor %}

When you complete the steps, to activate the email functionality, call MailFacade::handleMail().