02 March 2014 — Comments
Some time ago I created a Zend Framework 2 module designed to wrap Zend\Mail and ease sending emails in a Zend Framework 2 application. I was copy-pasting the code of that module in many applications so I decided it was a good candidate to become an independent module.
After creating it I pushed it to github and registered it in packagist, allowing it to be installed by using composer. Then I registered it in the official Zend Framework 2 modules web page. In this post I am going to explain how to use it and all the features it includes. The module can be found here.
It has grown in the last months, adding more features. File attachemnts, composition of emails from templates, event handling, SMTP over SSL, etc.
At the moment of writing this article the last published version is 1.8.2
AcMailer can be easily installed by using composer. Just add it to your composer.json file, in the require element.
{
"require": {
"acelaya/zf2-acmailer": "1.*"
}
}
Then run php composer.phar install
to get it downloaded in your vendor directory. Finally enable the module by adding it to your application.config.php file.
'modules' => array(
'AcMailer',
'Application',
...
),
The main object in this module is the AcMailer\Service\MailService
. It provides all the functionality to send emails by using Zend\Mail.
It depends on a Zend\Mail\Message
object, a Zend\Mail\Transport\TransportInterface
and a Zend\View\Renderer\RendererInterface
.
The last one is used to render views when composing the email from a template.
After creating the service instance, it can be used to set the body and the subject of the message. Any other data can be set by using the message object. For example.
<?php
// ...
$transport = new \Zend\Mail\Transport\Sendmail();
$message = new \Zend\Mail\Message();
$renderer = new \Zend\View\Renderer\PhpRenderer();
$message->setFrom("alejandro@domain.com", "Alejandro Celaya")
->setTo(array("address1@domain.com", "address2@domain.com"));
$service = new \AcMailer\Service\MailService($message, $transport, $renderer);
$service->setBody("<p>This is the body</p>")
->setSubject("This is the subject");
try {
$result = $service->send();
echo $result->isValid() ? "Success!!" : "Error";
} catch (\Exception $e) {
echo "Exception!! " . $e->getMessage();
}
This example shows how to manually create a MailService and use it to send an email, however the ZF2-AcMailer module includes a registered service that reads the configuration and creates a MailService instance ready to be used. It is registered as AcMailer\Service\MailService.
The AcMailer\Service\Factory\MailServiceFactory
is responsible for creating that preconfigured MailService. Take a look at it.
While setting the body, the MailService takes care to cast it to a proper object, accepting either a plain string, a HTML string, a Zend\Mime\Message
or a Zend\Mime\Part
.
If a template is set by calling the setTemplate
method, then the previous body will be discarded and the result of rendering that template will be used.
Files can be attached to the email before sending it, by calling addAttachment
, addAttachemnts
or setAttachments
methods, which will receive a file path or an array of file paths.
The registered service reads the configuration looking for a ‘mail_options' entry. An empty mail_options configuration array is included with the module at config/mail.global.php.dist. Feel free to customize it as you need. Every configuration option is explained at that file, but this are those options.
Zend\Mail\Transport\Sendmail
Sendmail
sendmail
Zend\Mail\Transport\Smtp
Smtp
smtp
false
to disable SSL, and 'ssl' or 'tls'.false
to disable this option.This configuration is mapped to an Options object at runtime, which is consumed by the MailServiceFactory. The Options object is registered in the ServiceManager with the key AcMailer\Options\MailOptions.
The module takes care to trigger three types of events. One event is triggered just before sending the email, one is triggered just after sending the email if everything was OK and anothert is triggered if an error occured while sending the email.
To handle those email events you just need to implement the AcMailer\Event\MailListener
interface, which provides three methods that are called when those three events are triggered. For example.
<?php
namespace Application\Mail\Event;
use AcMailer\Event\MailListener;
use AcMailer\Event\MailEvent;
class MyMailListener implements MailListener
{
public function osPreSend(MailEvent $e)
{
echo "The email is going to be sent";
}
public function osPostSend(MailEvent $e)
{
echo "The email was properly sent";
}
public function osSendError(MailEvent $e)
{
echo "An error occured while sending the email";
}
}
Then attach a MailListener to the MailService.
<?php
// ...
$mailService = $serviceManager->get("AcMailer\Service\MailService");
$mailService->attachMailListener(new \Application\Mail\Event\MyMailListener(), 5);
When the method send
is called, all the events are triggered in order of priority (the second argument) or in the order they were attached if they have the same priority.
The MailEvent object provided to any of the methods defined in the MailListener can be used to get the instance of the MailService who triggered that event, by calling the getMailService method.
<?php
namespace Application\Mail\Event;
use AcMailer\Event\MailListener;
use AcMailer\Event\MailEvent;
class MyMailListener implements MailListener
{
public function osPreSend(MailEvent $e)
{
// Get the service;
$mailService = $e->getMailService();
}
// ...
}
This can be useful to change destination addresses before sending the email if some condition happened, for example.
The MailService object should be injected in other services that will need to use it. To allow those services be properly tested the MailService implements an interface that can be used to define Mocks. Indeed a MailServiceMock is provided. Feel free to use it.
All the module's unit tests are located at the test folder and should pass while running them in the scope of a Zend Framework 2 application.