This module, once enabled, registers a service with the key AcMailer\Service\MailService
that wraps ZF2 mailing functionality, allowing to configure mail information to be used to send emails.
It supports file attachment and template email composition.
Install composer in your project
curl -s http://getcomposer.org/installer | php
Then run
php composer.phar require acelaya/zf2-acmailer:~4.0
Add the module AcMailer
to your config/application.config.php
file
<?php
return array(
// This should be an array of module namespaces used in the application.
'modules' => array(
'AcMailer',
'Application'
),
)
After installation, copy vendor/acelaya/zf2-acmailer/config/mail.global.php.dist
to config/autoload/mail.global.php
and customize any of the params. Configuration options are explained later.
Once you get the AcMailer\Service\MailService
service, a new MailService instance will be returned and you will be allowed to set the body, set the subject and then send the message.
$mailService = $serviceManager->get('AcMailer\Service\MailService');
$mailService->setSubject('This is the subject')
->setBody('This is the body'); // This can be a string, HTML or even a zend\Mime\Message or a Zend\Mime\Part
$result = $mailService->send();
if ($result->isValid()) {
echo 'Message sent. Congratulations!';
} else {
if ($result->hasException()) {
echo sprintf('An error occurred. Exception: \n %s', $result->getException()->getTraceAsString());
} else {
echo sprintf('An error occurred. Message: %s', $result->getMessage());
}
}
Inside controllers, you can access and use the MailService by using the sendMail
controller plugin. It returns the MailService when no arguments are provided.
// In a class extending Zend\Mvc\AbstractController...
$mailService = $this->sendMail();
$mailService->setSubject('This is the subject')
->setBody('This is the body'); // This can be a string, HTML or even a zend\Mime\Message or a Zend\Mime\Part
$result = $mailService->send();
But you can pass some basic information, making the email to be sent right away and the result to be returned.
// In a class extending Zend\Mvc\AbstractController...
$result = $this->sendMail(
'The body',
'The subject',
array('[email protected]', '[email protected]')
);
// Send another one
$result = $this->sendMail(array(
'subject' => 'Hello there!',
'from' => array('[email protected]', 'John Doe')
));
Adapters configuration can't be provided here, and sholuld be defined at configuration level. Any other information not provided here will be read from configuration.
The plugin accepts a maximum of 7 arguments, which are the body, the subject, the 'to', the 'from', the 'cc', the 'bcc' and the attachments. They can be provided as an associative array too.
Instead of setting a plain string, the body of the message can be set from a view script by using setTemplate
instead of setBody
. It will use a renderer to render defined template and then set it as the email body internally.
You can set the template as a string and pass the arguments for it.
$mailService = $serviceManager->get('AcMailer\Service\MailService');
$mailService->setSubject('This is the subject')
->setTemplate('application/emails/merry-christmas', array('name' => 'John Doe', 'date' => date('Y-m-d'));
You can also set the template as a Zend\View\Model\ViewModel
object, which will render child templates too.
$mailService = $serviceManager->get('AcMailer\Service\MailService');
$layout = new \Zend\View\Model\ViewModel(array(
'name' => 'John Doe',
'date' => date('Y-m-d')
));
$layout->setTemplate('application/emails/merry-christmas');
$footer = new \Zend\View\Model\ViewModel();
$footer->setTemplate('application/emails/footer');
$layout->addChild($footer, 'footer');
$mailService->setSubject('This is the subject')
->setTemplate($layout);
The renderer can be changed to another one (ie. Twig or Blade). It just needs to implement Zend\View\Renderer\RendererInterface
.
By default AcMailer uses the default ViewRenderer
service via an alias, mailviewrenderer
. You can override that alias in your service_manager
configuration in order to change the renderer service to be used (thanks to kusmierz):
return array(
'service_manager' => array(
'aliases' => array(
'mailviewrenderer' => 'ZfcTwigRenderer',
),
),
);
Alternatively you can just set it via setter: $mailService->setRenderer($renderer);
.
When running a ZF2 application from the console, the default ViewRenderer
service is not created. In that case a Zend\View\Renderer\PhpRenderer
is created on the fly so that templates can be properly rendered.
It has access to view_manager
and view_helpers
configuration, so template resolution will properly work and view helpers (both standard and custom) will be accessible from rendered templates.
If you overriden the mailviewrenderer
service alias with your own view renderer, then that will be used instead of creating one on the fly.
It is safe to use this module to send emails from cron jobs and such.
Files can be attached to the email before sending it by providing their paths with addAttachment
, addAttachments
or setAttachments
methods.
At the moment we call send
, all the files that already exist will be attached to the email.
$mailService->addAttachment('data/mail/attachments/file1.pdf');
$mailService->addAttachment('data/mail/attachments/file2.pdf', 'different-filename.pdf');
// Add two more attachments to the list
$mailService->addAttachments(array(
'another-name.pdf' => 'data/mail/attachments/file3.pdf',
'data/mail/attachments/file4.zip'
));
// At this point there is 4 attachments ready to be sent with the email
// If we call this, all previous attachments will be discarded
$mailService->setAttachments(array(
'data/mail/attachments/another-file1.pdf',
'name-to-be-displayed.png' => 'data/mail/attachments/another-file2.png'
));
// A good way to remove all attachments is to call this
$mailService->setAttachments(array());
The files will be attached with their real name unless you provide an alternative name as the key of the array element in addAttachments
and setAttachments
or as the second argument in addAttachment
.
Attention!! Be careful when attaching files to your email programatically.
Attached images can be displayed inmail by setting the cid
to the image filename in the image tag like this (thanks to omarev). The alternative name should be used if provided.
<img alt="This is an attached image" src="cid:image-filename.jpg">
If mail options does not fit your needs or you need to update them at runtime, the message wrapped by the MailService can be customized by getting it before calling send()
.
$message = $mailService->getMessage();
$message->addTo('[email protected]')
->addTo('[email protected]')
->addBcc('[email protected]');
$result = $mailService->send();
If you are using a Zend\Mail\Transport\File
as the transport object and need to change any option at runtime do this
$mailService = $serviceManager->get('AcMailer\Service\MailService');
$mailService->getTransport()->getOptions()->setPath('dynamically/generated/folder');
$result = $mailService->send();
The mail service can be automatically configured by using the provided global configuration file. Supported options are fully explained at that file. This is what they are for.
- mail_adapter: Tells the mail service what type of transport adapter should be used. Any object or classname implementing
Zend\Mail\Transport\TransportInterface
is valid. - mail_adapter_service: A service name to be used to get the transport object, in case standard transport configuration does not fit your needs. If defined, the mail_adapter option will be ignored.
- from: From email address.
- from_name: From name to be displayed.
- to: It can be a string with one destination email address or an array of multiple addresses.
- cc: It can be a string with one carbon copy email address or an array of multiple addresses.
- bcc: It can be a string with one blind carbon copy email address or an array of multiple addresses.
- subject: Default email subject.
- body: Default body to be used. Usually this will be generated at runtime, but can be set as a string at config file. It can contain HTML too.
- body_charset: The charset to be set to default body if it is a string. Default value is 'utf-8'.
- template: Array with template configuration. It has 4 child options.
- use_template: True or false. Tells if template should be used, making the body option to be ignored.
- path: Path of the template. The same used while setting the template of a ViewModel (ie. 'application/index/list').
- params: Array with key-value pairs with parameters to be sent to the template.
- children: Array with children templates to be used within the main template (layout). Each one of them can have its own children. Look at
vendor/acelaya/zf2-acmailer/config/mail.global.php.dist
for details.
- attachments: Allows to define an array of files that will be attached to the message, or even a directory that will be iterated to attach all found files.
- files: Array of files to be attached. Can be an associative array where keys are attachment names and values are file paths.
- dir: Directory to iterate.
- iterate: If it is not true, the directory won't be iterated.
- path: The path of the directory to iterate looking for files. This files will be attached with their real names.
- recursive: True or false. Tells if nested directories should be iterated too.
- server: IP address or server name to be used while using a SMTP server. Only used for SMTP transport.
- port: SMTP server port while using SMTP transport.
- smtp_user: Username to be used for authentication against SMTP server. If none is provided the
from
option will be used for this purpose. - smtp_password: Password to be used for authentication against SMTP server.
- ssl: Defines type of connection encryption against SMTP server. Values are
false
to disable SSL, and 'ssl' or 'tls'. - connection_class: The connection class used for authentication while using SMTP transport. Values are 'smtp', 'plain', 'login' or 'crammd5'
- file_path: Directory where the email will be saved while using a File transport.
- file_callback: Callback used to get the filename while using File transport.
Many of the configuration options are specific for standard transport objects. If you are using a custom Zend\Mail\Transport\TransportInterface
implementation, you can set the mail_adapter_service instead of the mail_adapter option, to define the service which returns the transport object.
This module comes with a built-in event system.
- An event is triggered before the mail is sent (
MailEvent::EVENT_MAIL_PRE_SEND
). - If everything was OK another event is triggered (
MailEvent::EVENT_MAIL_POST_SEND
) after the email has been sent. - If an error occured, an error event is triggered (
MailEvent::EVENT_MAIL_SEND_ERROR
).
Managing mail events is as easy as extending AcMailer\Event\AbstractMailListener
. It provides the onPreSend
, onPostSend
and onSendError
methods, which get a MailEvent
parameter that can be used to get the MailService
which triggered the event or the MailResult
produced.
Then attach the listener object to the MailService
and the corresponding method will be automatically called when calling the send
method.
$mailListener = new \Application\Event\MyMailListener();
$mailService->attachMailListener($mailListener);
$mailService->send(); // Mail events will be triggered at this moment
If you want to detach a previously attached event manager, just call detach MailListener like this.
$mailListener = new \Application\Event\MyMailListener();
$mailService->attachMailListener($mailListener);
// Some conditions occurred which made us not to want the events to be triggered any more on this listener
$mailService->detachMailListener($mailListener);
$mailService->send(); // The events on the $mailListener won't be triggered.
The MailResult
will always be null when the event EVENT_MAIL_PRE_SEND
is triggered, since the email hasn't been sent yet.
Any Zend\Mail
exception will be catched, producing a EVENT_MAIL_SEND_ERROR
instead. If any other kind of exception occurs, the same event will be triggered, but the exception will be rethrown in the form of an AcMailer\Exception\MailException
. The event's wrapped exception will be the original exception.
AcMailer\Service\MailService
should be injected into Controllers or other Services which you probably need to test. It implements AcMailer\Service\MailServiceInterface
for this purpose, but even a MailServiceMock
is included.
It allows user to define if the message should or should not fail when send
method is called, by calling setForceError
method.
You can even know if send
method was called after any action by calling isSendMethodCalled
.
$mailServiceMock = new \AcMailer\Service\MailServiceMock();
$mailServiceMock->isSendMethodCalled(); // This will return false at this point
// Force an error
$mailServiceMock->setForceError(true);
$result = $mailServiceMock->send();
$result->isValid(); // This will return false because we forced an error
$mailServiceMock->isSendMethodCalled(); // This will return true at this point
// Force a success
$mailServiceMock->setForceError(false);
$result = $mailServiceMock->send();
$result->isValid(); // This will return true in this case
Thanks to PhpStorm for their support, since they have provided me an OSS license to work on this project.