Emails
For sending emails this module provides standardised ways to:
- send single emails (password reset email, order confirmation email, user defined mail)
- send bulk emails (event canceled email, user defined mail)
- send test emails
All emails are created from templates, so called template mailables. We will call them just
mailables in all further documentation. Every kind of email has it’s
own mailable (PasswordResetMailabe
, UserFreeMailable
etc.).
Creating mailables
A mailable consists of:
- a human readable label and description
- a mailable data class
- a default subject and body provided by a default template
- a placeholder provider class
- static attachments (optional)
- dynamic attachments (optional)
A new mailable class is created by extending the abstract TemplateMailable
.
Each mailable has a $label
and a $description
. Their value should be
a translation string. You can use the static methods getLabel()
and getDescription
to display a translated label and description of the mailable to your users.
<?php
namespace JohnIt\Bc\Core\Domain\Mailables;
use JohnIt\Bc\Core\Domain\Mailables\Contracts\TemplateMailable;
class PasswordResetMailable extends TemplateMailable
{
public static string $label = 'bc-core-domain::user.mails.resetPassword.name';
public static string $description = 'bc-core-domain::user.mails.resetPassword.description';
}
Mailable data
A MailableData
is the data source of a mailable. It is used for generating
placeholder values and it is also passed to the attachments of a mailable.
The MailableData
is passed as second parameter to the constructor of a mailable.
<?php
namespace JohnIt\Bc\Core\Domain\DataTransferObjects;
use Illuminate\Support\Facades\Password;
use JohnIt\Bc\Core\Domain\Mailables\Contracts\MailableData;
use JohnIt\Bc\Core\Domain\Models\User;
class PasswordResetMailableData extends MailableData
{
public readonly string $token;
public function __construct(
public readonly string $email,
public readonly string $name,
?string $token = null
) {
$this->token = $token ?? Password::createToken(User::where('email', $email)->firstOrFail());
}
public static function makeDummy(): static
{
return new static(
name: 'Max Mustermann',
email: 'max.mustermann@john-it.com',
token: 'b1063f85cfcf5b1113a81524036b941cc4f7d9e7d7b24c9d1f28ed02aa3d041f',
);
}
}
Each mailable data must provide a static makeDummy
factory method to generate
data for test emails. To generate dummy mails a mailable must know which
MailableData
to use. So the fully qualyfied class name must be
configured in MailableTemplate::$dataClass
:
<?php
namespace JohnIt\Bc\Core\Domain\Mailables;
use JohnIt\Bc\Core\Domain\Mailables\Contracts\TemplateMailable;
use JohnIt\Bc\Core\Domain\DataTransferObjects\PasswordResetMailableData;
class PasswordResetMailable extends TemplateMailable
{
public static string $label = 'bc-core-domain::user.mails.resetPassword.name';
public static string $description = 'bc-core-domain::user.mails.resetPassword.description';
public static string $dataClass = PasswordResetMailableData::class;
}
Placeholder provider
Subject and body of a mailable can contain placeholders to personalize emails. This placeholders are generated using a placeholder provider. The placeholder provider consumes the data provided by the mailable data to generate the placeholder values.
<?php
namespace JohnIt\Bc\Core\Domain\Placeholders;
use JohnIt\Bc\Core\Domain\DataTransferObjects\PasswordResetMailableData;
use JohnIt\Bc\Core\Domain\Placeholders\PlaceholderProvider;
use function e;
class PasswordResetPlaceholderProvider extends PlaceholderProvider
{
protected static array $placeholders = [
'UserName' => 'bc-core-domain::user.placeholders.UserName',
'ResetLink' => 'bc-core-domain::user.mails.invite.placeholders.ActivationUrl.description',
'SoftwareName' => 'Software name'
];
public function __construct(
private PasswordResetMailableData $data,
)
{}
public static function makeDummy(): static
{
return new static(PasswordResetMailableData::makeDummy());
}
protected function hydrate(): static
{
$this->values = collect([
'UserName' => $this->data->name,
'ResetLink' => sprintf(
'<a href="%s">%s</a>',
route('users.reset-password', [
'token' => $this->data->token,
'email' => $this->data->email,
]),
e(trans('bc-core-domain::user.mails.invite.labels.reset')),
),
'SoftwareName' => config('app.name'),
]);
return $this;
}
}
To generate placeholders a mailable must know which
PlaceholderProvider
to use. So the fully qualyfied class name must be
configured in MailableTemplate::$placeholderProviderClass
:
<?php
namespace JohnIt\Bc\Core\Domain\Mailables;
use JohnIt\Bc\Core\Domain\Mailables\Contracts\TemplateMailable;
use JohnIt\Bc\Core\Domain\DataTransferObjects\PasswordResetMailableData;
use JohnIt\Bc\Core\Domain\PlaceholderProviders\PasswordResetPlaceholderProvider;
class PasswordResetMailable extends TemplateMailable
{
public static string $label = 'bc-core-domain::user.mails.resetPassword.name';
public static string $description = 'bc-core-domain::user.mails.resetPassword.description';
public static string $dataClass = PasswordResetMailableData::class;
public static string $placeholderProviderClass = PasswordResetPlaceholderProvider::class;
}
Default template
All mailables must provide subject and body. This is done by providing a default
MailTemplate
. Placeholders defined by the placeholder provider can be used
inside subject and body.
<?php
namespace JohnIt\Bc\Core\Domain\Mailables;
use JohnIt\Bc\Core\Domain\Mailables\Contracts\TemplateMailable;
use JohnIt\Bc\Core\Domain\Models\MailTemplate;
use JohnIt\Bc\Core\Domain\DataTransferObjects\PasswordResetMailableData;
use JohnIt\Bc\Core\Domain\PlaceholderProviders\PasswordResetPlaceholderProvider;
class PasswordResetMailable extends TemplateMailable
{
public static string $label = 'bc-core-domain::user.mails.resetPassword.name';
public static string $description = 'bc-core-domain::user.mails.resetPassword.description';
public static string $dataClass = PasswordResetMailableData::class;
public static string $placeholderProviderClass = PasswordResetPlaceholderProvider::class;
public function defaultTemplate(): MailTemplate
{
return MailTemplate::make([
'subject' => 'Aktivierung Ihres neuen Benutzerkontos',
'template_html' => ' <p>Hallo {{UserName}},</p>'.
'<p>Es wurde ein neues Benutzerkonto in der Software {{{ SoftwareName }}} für Sie erstellt. Bitte aktivieren Sie Ihr Konto über folgenden Link:</p>'.
'<p style="margin-top: 40px;margin-bottom: 40px; text-align: center;" > {{{ActivationLink}}}</p>'.
'<p>Dieser Aktivierungslink ist 2 Wochen gültig. Danach wenden Sie sich bitte an Ihren Administrator.</p>',
]);
}
}
Sending mailables
Usually there are two ways to instantiate a new mailable object:
- using a factory method
- manually instantiating a mailable using the
new
keyword
Sending test mails
All mailables provide a factory method to create a dummy mailable for testing purposes. In example for sending a preview. Dummy mailables use example data for placeholders and attachments:
use Illuminate\Support\Facades\Mail;
use JohnIt\Bc\Core\Domain\Mailables\PasswordResetMailable;
$mailable PasswordResetMailable::makeDummy();
Mail::to('test@john-it.com')->send($mailable);
Optionally you can pass a so called Envelope
to the factory method
to provide recipient information:
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Support\Facades\Mail;
use JohnIt\Bc\Core\Domain\Mailables\PasswordResetMailable;
$envelope = new Envelope(to: $user->email);
$mailable PasswordResetMailable::makeDummy($envelope);
Mail::send($mailable);
Sending mails to Notifiables
Mailables used for bulk mails usually provide a factory method for sending
mailables to notifiables.
Notifiables are classes implementing the NotifiableContract
interface which
allows to automatically determine the recipient from the notifiable.
Users, Contacts but also Licenses are notifiables.
use Illuminate\Support\Facades\Mail;
use JohnIt\Bc\Core\Domain\Mailables\PasswordResetMailable;
Mail::send(PasswordResetMailable::makeFromNotifiable($user));
Optionally you can pass an envelope to the factory method to specify CC, BCC, Reply-to or additional TO addresses.
use Illuminate\Support\Facades\Mail;
use JohnIt\Bc\Core\Domain\Mailables\PasswordResetMailable;
use Illuminate\Mail\Mailables\Envelope;
$envelope = new Envelope(replyTo: 'reply@john-it.com');
Mail::send(PasswordResetMailable::makeFromNotifiable($user, $envelope));
More advanced sending method
If none of the factory methods is siutable or available for you then you can always manually instantiate a mailable. In the first parameter an envelope must be provided. In the second parameter you pass the data the mailable needs for generating placeholders and attachments:
use Illuminate\Support\Facades\Mail;
use JohnIt\Bc\Core\Domain\DataTransferObjects\PasswordResetMailableData;
use JohnIt\Bc\Core\Domain\Mailables\PasswordResetMailable;
use Illuminate\Mail\Mailables\Envelope;
// Create envelop obejct
$envelope = new Envelope(to: $user->email);
// Create data object used by placeholders and attachments.
// The PasswordResetMailableData also creates the password reset token.
$userInviteData = new PasswordResetMailableData(
email: $user->email,
name: $user->name,
);
// Create mailable
$mailable new PasswordResetMailable(
$envelope,
$userInviteData
);
// send maiable
Mail::send($mailable);
If you’d like to manually send a mail using test data you can always call the
makeDummy
factory method on the coresponding data classes:
use JohnIt\Bc\Core\Domain\DataTransferObjects\PasswordResetMailableData;
$userInviteData = PasswordResetMailableData::makeDummy();
Attachments
Static attachments
Dynamic attachments
Domain model
Project makes use of a package called by Spatie and extends it to allow custom placeholders and attachments. Make sure to read documenation for and before continuing.
All Mailables in a project need to inherit from \Domain\Core\Mails\Mailable
class if there is no specific need not to do so.
Lets look at \Domain\Core\Mails\InviteUserMail
as an example. It overrides $name
and $description
properties as well as
following methods:
- default()
: This method needs to return a default instance of this class. That object is used for editing and previewing in user interface.
- defaultTemplate()
: Return a default MailTemplate
with subject
and template_html
keys set. You can make use of placeholders both in subject
and template_html
.
- defaultAttachments()
: Return a Collection
of \JohnIt\Bc\Core\Domain\Models\Document
descendants to be dynamically attached to this Mailable
.
- customPlaceholders()
: Add a list of \Domain\Core\Placeholder
classes to parent Collection
and return it. These placeholders can be used both when sending mass e-mails or inside defaultTemplate()
method.
Sending Mailable
is no different from sending it using default Laravel methods as described in Laravel documentation.