Automation tool for e-mail distribution, queue management and advanced logging for Nette Framework with Doctrine ORM.
- Queue-based delivery - E-mails are stored in database and sent asynchronously via daemon process
- Template rendering - Support for Latte, HTML and plain text templates with localization
- Retry mechanism - Failed e-mails are automatically retried up to 5 times with exponential backoff
- Logging & monitoring - Complete audit trail of all sent e-mails with timing metrics
- Garbage collection - Automatic cleanup of old logs and message bodies
- Recipient fixing - Automatic typo correction for common e-mail domains (gmail.com, seznam.cz, etc.)
+------------------+ +------------------+ +------------------+
| | | | | |
| Your App Code |---->| Emailer |---->| Database Queue |
| | | | | (Doctrine ORM) |
+------------------+ +------------------+ +------------------+
|
v
+------------------+ +------------------+ +------------------+
| | | | | |
| SMTP Server |<----| Queue Runner |<----| Emailer Daemon |
| / Sendmail | | | | (CLI Command) |
+------------------+ +------------------+ +------------------+
| Component | Description |
|---|---|
Emailer |
Main service implementing Nette\Mail\Mailer interface. Handles sending e-mails directly or via queue. |
Message |
Extended Nette\Mail\Message with support for priority, scheduled sending and localization. |
QueueRunner |
Processes e-mail queue, handles retries and logs results. |
EmailerDaemon |
Symfony Console command for running queue processor as daemon. |
TemplateRenderer |
Renders e-mail templates using pluggable renderers (Latte, HTML, TXT). |
EmailerLogger |
Persists logs to database with INFO/WARNING/ERROR levels. |
GarbageCollector |
Cleans old logs and message bodies to keep database size manageable. |
Fixer |
Interface for e-mail address correction (typo fixing). |
| Entity | Table | Purpose |
|---|---|---|
Email |
core__emailer_email |
Queue entry with status, attempts count, timing metrics |
Log |
core__emailer_log |
Log entries linked to e-mails |
Configuration |
- | Runtime configuration (not persisted) |
It's best to use Composer for installation, and you can also find the package on Packagist and GitHub.
To install, simply use the command:
$ composer require baraja-core/emailerYou can use the package manually by creating an instance of the internal classes, or register a DIC extension to link the services directly to the Nette Framework.
- PHP 8.0+
- Nette Framework 3.0+
- Doctrine ORM
- Latte 2.5+
Register the extension in your config.neon:
extensions:
emailer: Baraja\Emailer\EmailerExtension
emailer:
useQueue: true
defaultFrom: [email protected]
adminEmails:
- [email protected]
mail:
smtp: true
host: smtp.example.com
port: 465
username: [email protected]
password: secret
secure: ssl| Option | Type | Default | Description |
|---|---|---|---|
useQueue |
bool | true |
Enable queue mode (recommended for production) |
defaultFrom |
string | - | Default sender address |
adminEmails |
array | [] |
Admin e-mails for system notifications |
mail |
array | - | SMTP configuration (same as Nette mailer) |
use Baraja\Emailer\Emailer;
use Baraja\Emailer\Message;
class OrderService
{
public function __construct(
private Emailer $emailer,
) {}
public function sendConfirmation(string $to): void
{
$message = new Message;
$message->setFrom('[email protected]');
$message->addTo($to);
$message->setSubject('Order Confirmation');
$message->setHtmlBody('<h1>Thank you for your order!</h1>');
$this->emailer->send($message);
}
}$message = new Message;
$message->setPriority(Message::URGENT);
$message->setSubject('Critical Alert');
// ...
$this->emailer->send($message); // Sent immediatelyCreate a reusable e-mail service by extending BaseEmail:
use Baraja\Emailer\Email\BaseEmail;
use Baraja\Emailer\Message;
class WelcomeEmail extends BaseEmail
{
public function getName(): string
{
return 'Welcome Email';
}
public function getDescription(): ?string
{
return 'Sent to new users after registration';
}
public function getTemplate(string $locale): ?string
{
return __DIR__ . "/templates/welcome.{$locale}.latte";
}
public function getMessage(): Message
{
$message = parent::getMessage();
$message->setSubject('Welcome to Our Service');
return $message;
}
}Then send it:
$this->emailer
->getEmailServiceByType(WelcomeEmail::class, [
'to' => '[email protected]',
'userName' => 'John',
])
->send();$message = new Message;
$message->setSendEarliestAt('tomorrow 9:00');
// or
$message->setSendEarliestAt(new \DateTimeImmutable('+2 hours'));
$this->emailer->send($message);$this->emailer->sendMessageToAdministrators(
subject: 'Server Alert',
message: 'Disk space is running low!',
additionalEmails: ['[email protected]'],
);Set up a cron job to run the e-mail daemon:
# Run every 5 minutes
*/5 * * * * php bin/console baraja:emailer-daemonThe daemon will:
- Process queued e-mails for ~5 minutes (configurable timeout)
- Handle retries for failed e-mails
- Run garbage collection after processing
Adjust queue behavior via Configuration entity:
| Setting | Default | Description |
|---|---|---|
queueTimeout |
295s | How long daemon runs before exiting |
queueEmailDelay |
0.3s | Delay between sending e-mails |
queueCheckIterationDelay |
2s | Delay when queue is empty |
maxAllowedAttempts |
5 | Max retry attempts before marking as failed |
| Format | Extension | Renderer |
|---|---|---|
| Latte | .latte |
LatteRenderer |
| HTML | .html |
HtmlRenderer |
| Plain Text | .txt |
TextRenderer |
{* templates/welcome.en.latte *}
<h1>Welcome, {$userName}!</h1>
<p>Thank you for joining our service.</p>
<p><a n:href="Front:Homepage:default">Visit our website</a></p>Use {_} tags for translations:
<p>{_}email.welcome.greeting{/_}</p>
<p>{_'email.welcome.message'}</p>Implement Renderer interface and tag it:
services:
-
factory: App\Email\MjmlRenderer
tags: [emailer-renderer]| Status | Description |
|---|---|
in-queue |
Waiting to be sent |
not-ready-to-queue |
Scheduled for future |
waiting-for-next-attempt |
Failed, will retry |
sent |
Successfully delivered |
preparing-error |
Template/build error |
sending-error |
SMTP/delivery error |
Implement Fixer interface to customize e-mail correction:
use Baraja\Emailer\RecipientFixer\Fixer;
class MyFixer implements Fixer
{
public function fix(string $email): string
{
// Your correction logic
return $email;
}
}Register in DI container and it will be used automatically.
All e-mail operations are logged with three severity levels:
INFO- Successful operationsWARNING- Non-critical issuesERROR- Failed operations
Logs are automatically cleaned up:
- Common logs: after 14 days
- E-mail-related logs: after 3 months
- Message bodies: after 3 months
Jan Barasek (https://baraja.cz)
baraja-core/emailer is licensed under the MIT license. See the LICENSE file for more details.