Sending notification e-mails to a list

We’ll add one more feature in this tutorial. We need to notify someone that a user has sent a message.

We won’t add the e-mail sending logic directly in the controller or service class. We want to do this in a more decoupled way. And by doing so, we will also cover a bit more of Zend Framework and DotKernel.

What we’ll cover in this lesson are the mail services provided by our dot-mail package and listening to events fired by the mappers.

We’ll cover how to fire your own events in another lesson. For now, we will rely on already defined events that are fired by our mappen during its operations.

We’ll use one of the mapper’s save events in order to send a notification e-mail to a predefined list of e-mail addresses. Events are great to completely decouple code. They also provide an extension point and code can be added, without changing the original code, by injecting features that are sometimes not even related to the main functionality. Also the decoupling is good because the code that saves the user message to the database should not know about what happens when the event is fired. You can add anything, from logging to email notifications or additional custom code that should run before or after a particular operation, all through the event listeners.

Creating a notification e-mail service class

We will wrap the default mail service into a higher level service class which compose the mail message that needs to be sent on various notification ocasions. Create a new PHP class file in the Service folder

NotificationMailService.php

declare(strict_types=1);

namespace Frontend\App\Service;

use Dot\AnnotatedServices\Annotation\Inject;
use Dot\AnnotatedServices\Annotation\Service;
use Dot\Mail\Service\MailService;
use Frontend\App\Entity\UserMessageEntity;

/**
 * Class NotificationMailService
 * @package Frontend\App\Service
 *
 * @Service
 */
class NotificationMailService
{
    /** @var  MailService */
    protected $mailService;

    /** @var array  */
    protected $notificationList = [];

    /**
     * NotificationMailService constructor.
     * @param MailService $mailService
     * @param array $notificationReceivers
     *
     * @Inject({"dot-mail.service.default", "config.contact.notification_receivers"})
     */
    public function __construct(MailService $mailService, array $notificationReceivers = [])
    {
        $this->mailService = $mailService;
        $this->notificationList = $notificationReceivers;
    }

    /**
     * @param UserMessageEntity $userMessage
     * @return bool
     */
    public function sendUserMessageNotificationEmail(UserMessageEntity $userMessage)
    {
        if (empty($this->notificationList)) {
            return true;
        }

        $message = $this->mailService->getMessage();
        $message->setFrom($userMessage->getEmail(), $userMessage->getName());
        $message->setTo($this->notificationList);

        $message->setSubject("DotKernel notification");
        $this->mailService->setBody(sprintf(
            "A new user message from %s was submitted!" .
            "<br><br><strong>Subject:</strong>" .
            "<br>%s" .
            "<br><br><strong>Message:</strong>" .
            "<br>%s",
            $userMessage->getName(),
            $userMessage->getSubject(),
            nl2br($userMessage->getMessage())
        ));

        $result = $this->mailService->send();
        return $result->isValid();
    }
}

A few things to note about this class

  • We use our annotation service component to automatically inject dependencies without a factory class
  • We use the default mail service, which you already have configured if you are using the frontend application
  • We inject a configuration key that we don’t have it defined yet, which will be the receiver email addresses as an array

In your local.php file define the notification receiver list(the admins or someone responsible to manage user messages)

local.php

return [
    'contact' => [
        'notification_receivers' => [
            '[email protected]',
            //...
        ]
    ]
];

We haven’t defined an interface for this class. We could have done that but for now it not necessary, as it’s just an example. We are ready to listen to events and send notification emails.

Listening to mapper events

DotKernel mappers fire various events during their lifetime. You can hook into these events to add aditional functionality.

For our case we are interested in the onAfterSaveCommit event, this is when we are sure that the entity was successfully inserted into the database.

Mapper event listeners must implement the MapperEventListenerInterface provided by the mapper package. We recommend you to extend the AbstractMapperEventListener or use the MapperEventListenerTrait to have a good starting point. Create a new folder in the App module, call it Listener. In this folder, create a new PHP class file

UserMessageMapperEventListener.php

declare(strict_types=1);

namespace Frontend\App\Listener;

use Dot\AnnotatedServices\Annotation\Inject;
use Dot\AnnotatedServices\Annotation\Service;
use Dot\Mapper\Event\AbstractMapperEventListener;
use Dot\Mapper\Event\MapperEvent;
use Frontend\App\Service\NotificationMailService;

/**
 * Class UserMessageMapperEventListener
 * @package Frontend\App\Listener
 *
 * @Service
 */
class UserMessageMapperEventListener extends AbstractMapperEventListener
{
    /** @var  NotificationMailService */
    protected $notificationMailService;

    /**
     * UserMessageMapperEventListener constructor.
     * @param NotificationMailService $notificationMailService
     *
     * @Inject({NotificationMailService::class})
     */
    public function __construct(NotificationMailService $notificationMailService)
    {
        $this->notificationMailService = $notificationMailService;
    }

    /**
     * @param MapperEvent $e
     */
    public function onAfterSaveCommit(MapperEvent $e)
    {
        $message = $e->getParam('entity');
        $this->notificationMailService->sendUserMessageNotificationEmail($message);
    }
}

This class is self-explanatory. We are using annotations to inject the notification mail service defined earlier. This class can listen to any mapper events. Just implement the ones you are interested in. In the save event, we get the saved entity (check the dot-mapper documentation) and call the send notification method.

We have one last thing to do, tell the user message mapper that this is a listener of its events. There are more than one possible solution. The dot-mapper package provides an easy method for doing this, simply go to the ConfigProvider mapper section and add the following configuration

ConfigProvider.php

public function getMappers(): array
{
    return [
        'mapper_manager' => [
            'factories' => [
                UserMessageDbMapper::class => DbMapperFactory::class,
            ],
            'aliases' => [
                UserMessageEntity::class => UserMessageDbMapper::class,
            ]
        ],
        'options' => [
            UserMessageEntity::class => [
                'mapper' => [
                    'event_listeners' => [
                        UserMessageMapperEventListener::class,
                    ]
                ]
            ]
        ]
    ];
}

The lines that we have added are the ones from the options key. For a complete description of possible configuration options please visit the dot-mapper documentation.

As you can see the options are defined per mapper/entity. The event_listeners key is used to attach your defined event listeners to the listener chain.

If you have configured the notification receivers, you should now receive an email when the contact form is successfully submitted.

Previous

Prev: Implementing the service class

Tutorial index

Go to the tutorial page

All tutorials

View all tutorials by going to the tutorials list


This page was generated by GitHub Pages.