PHPackages                             glueful/email-notification - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Mail &amp; Notifications](/categories/mail)
4. /
5. glueful/email-notification

ActiveGlueful-extension[Mail &amp; Notifications](/categories/mail)

glueful/email-notification
==========================

Provides email notification capabilities using Symfony Mailer

v1.10.0(2w ago)12101MITPHPPHP ^8.3

Since Sep 13Pushed 2w agoCompare

[ Source](https://github.com/glueful/email-notification)[ Packagist](https://packagist.org/packages/glueful/email-notification)[ Docs](https://github.com/glueful/email-notification)[ RSS](/packages/glueful-email-notification/feed)WikiDiscussions main Synced today

READMEChangelog (10)Dependencies (21)Versions (16)Used By (1)

Email Notification Extension for Glueful
========================================

[](#email-notification-extension-for-glueful)

Overview
--------

[](#overview)

The EmailNotification extension provides a modern email delivery system for the Glueful Framework's notification system. Built on **Symfony Mailer**, it features robust multi-provider support, failover, and an extensible transport architecture.

> Built on Symfony Mailer with modern provider-bridge support. Implements the framework's notification channel contract (`RichNotificationChannel`) with structured `NotificationResult`s.

> Deprecation notice
>
> - `manifest.json` is deprecated and removed in favor of Composer discovery (`extra.glueful.provider`).
> - The top-level `EmailNotification` class has been removed; use the ServiceProvider-driven registration. Code that previously referenced `Glueful\\Extensions\\EmailNotification` should instead use the framework's notification system and/or resolve `EmailNotificationProvider` via DI.

Features
--------

[](#features)

- ✅ **Modern Symfony Mailer Integration** - Enterprise-grade email infrastructure
- ✅ **Multi-Provider Support** - Brevo, SendGrid, Mailgun, Amazon SES, Postmark, and custom providers
- ✅ **Provider Bridges** - Native API integrations for optimal performance and reliability
- ✅ **Failover** - Multiple transport support with automatic failover
- ✅ **Extensible Architecture** - Support for any Symfony Mailer provider bridge
- ✅ **Advanced Template System** - Responsive templates with auto-escaped variable substitution and conditional logic
- ✅ **Recipient Domain Policy** - Allow-list / block-list enforcement on every recipient (primary, cc, bcc) before send
- ✅ **Attachment Path Confinement** - Attachment/embed paths confined to allowed directories
- ✅ **Developer Experience** - Clear error messages, debugging tools, and type safety

Requirements
------------

[](#requirements)

- PHP 8.3 or higher with strict typing
- Glueful Framework 1.51.0 or higher
- OpenSSL PHP extension
- Symfony Mailer (included)
- Composer for provider bridge dependencies

Installation
------------

[](#installation)

### Install via Composer (Recommended)

[](#install-via-composer-recommended)

```
composer require glueful/email-notification
```

### Enabling the extension

[](#enabling-the-extension)

Installing the package does **not** auto-load it — its provider must be in `config/extensions.php`'s `enabled` allow-list.

**Development (recommended):** the CLI edits `config/extensions.php` and recompiles the cache for you (it validates the change first, so it won't leave the config broken):

```
php glueful extensions:enable email-notification

# To disable (removes the provider from `enabled`):
php glueful extensions:disable email-notification
```

**By hand / in production:** add the provider as a plain string FQCN (no `::class`) to the `enabled` list, then build the manifest in your deploy step:

```
// config/extensions.php
return [
    'enabled' => [
        'Glueful\\Extensions\\EmailNotification\\EmailNotificationServiceProvider',
        // other providers...
    ],
];
```

```
php glueful extensions:cache   # required in production; boot fails fast without it
```

Verify discovery and state:

```
php glueful extensions:list
php glueful extensions:info email-notification
```

Note: `enable`/`disable` are dev-only conveniences and are disabled in production — there, manage the `enabled` list in config and run `extensions:cache`.

### Provider Bridge Installation

[](#provider-bridge-installation)

Install required Symfony provider bridges based on your email providers:

```
# For Brevo (Sendinblue)
composer require symfony/brevo-mailer

# For SendGrid
composer require symfony/sendgrid-mailer

# For Mailgun
composer require symfony/mailgun-mailer

# For Amazon SES
composer require symfony/amazon-mailer

# For Postmark
composer require symfony/postmark-mailer
```

Configuration
-------------

[](#configuration)

### Environment Variables

[](#environment-variables)

Configure your email providers in your `.env` file:

```
# Email Provider Selection
MAIL_MAILER=brevo

# Brevo Configuration (Sendinblue)
BREVO_TRANSPORT=brevo+smtp         # or brevo+api
BREVO_API_KEY=your-brevo-api-key
MAIL_USERNAME=your-smtp-username
MAIL_PASSWORD=your-smtp-password

# Generic SMTP Configuration
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_ENCRYPTION=tls               # tls, ssl, or none
MAIL_USERNAME=your-email@example.com
MAIL_PASSWORD=your-app-password

# Email Addresses
MAIL_FROM=noreply@example.com
MAIL_FROM_NAME=Your Application

# Extension behavior
MAIL_DEBUG=false              # debug-log each outgoing email (recipient/subject/type only)
MAIL_LOG_RESULTS=false        # info/error-log each send outcome (recipient/subject/type only)

# Recipient domain policy (optional; see Security Features)
# MAIL_ALLOWED_DOMAINS=yourcompany.com,partner.com
# MAIL_BLOCKED_DOMAINS=example.com,spam.test
```

### Services Configuration

[](#services-configuration)

Configure multiple email providers in `config/services.php`:

```
return [
    'mail' => [
        'default' => env('MAIL_MAILER', 'smtp'),

        'mailers' => [
            // Generic SMTP
            'smtp' => [
                'transport' => 'smtp',
                'host' => env('MAIL_HOST'),
                'port' => env('MAIL_PORT', 587),
                'encryption' => env('MAIL_ENCRYPTION', 'tls'),
                'username' => env('MAIL_USERNAME'),
                'password' => env('MAIL_PASSWORD'),
            ],

            // Brevo (Sendinblue) - API or SMTP
            'brevo' => [
                'transport' => env('BREVO_TRANSPORT', 'brevo+api'),
                'key' => env('BREVO_API_KEY'),
                'username' => env('MAIL_USERNAME'),
                'password' => env('MAIL_PASSWORD'),
                'dsn' => env('BREVO_DSN'), // Override for custom DSN
            ],

            // SendGrid
            'sendgrid' => [
                'transport' => 'sendgrid+api',
                'key' => env('SENDGRID_API_KEY'),
            ],

            // Mailgun
            'mailgun' => [
                'transport' => 'mailgun+api',
                'domain' => env('MAILGUN_DOMAIN'),
                'key' => env('MAILGUN_SECRET'),
                'region' => env('MAILGUN_REGION', 'us'),
            ],

            // Amazon SES
            'ses' => [
                'transport' => 'ses+api',
                'key' => env('AWS_ACCESS_KEY_ID'),
                'secret' => env('AWS_SECRET_ACCESS_KEY'),
                'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
            ],

            // Postmark
            'postmark' => [
                'transport' => 'postmark+api',
                'token' => env('POSTMARK_TOKEN'),
            ],
        ],

        'from' => [
            'address' => env('MAIL_FROM', 'noreply@example.com'),
            'name' => env('MAIL_FROM_NAME', 'Glueful Application'),
        ],

        // Failover configuration
        'failover' => [
            'mailers' => explode(',', env('MAIL_FAILOVER_MAILERS', '')),
        ],
    ],
];
```

### Custom Provider Support

[](#custom-provider-support)

The extension supports any Symfony Mailer provider bridge through three methods:

#### 1. Custom DSN (Most Flexible)

[](#1-custom-dsn-most-flexible)

```
'custom_provider' => [
    'transport' => 'custom',
    'dsn' => 'mandrill+api://your-api-key@default',
],
```

#### 2. Auto-Configuration (Standard Patterns)

[](#2-auto-configuration-standard-patterns)

```
'office365' => [
    'transport' => 'office365+smtp',
    'username' => 'user@company.com',
    'password' => 'password',
    // Auto-builds: office365+smtp://user:password@default
],
```

#### 3. Explicit Support (Built-in)

[](#3-explicit-support-built-in)

Already supported providers work without additional configuration.

Usage
-----

[](#usage)

### Basic Email Sending

[](#basic-email-sending)

The extension integrates seamlessly with Glueful's notification system:

```
use Glueful\Notifications\NotificationService;

$notificationService = container()->get(NotificationService::class);

// Simple email notification
$notificationService->send(
    'account_activity',
    $user,
    'Account Login Alert',
    [
        'message' => 'Your account was accessed from a new device.',
        'location' => 'San Francisco, CA',
        'device' => 'iPhone 13',
        'timestamp' => '2024-06-21 14:30:00',
    ],
    ['channels' => ['email']]
);
```

### Template-Based Emails

[](#template-based-emails)

Use professional templates for rich email experiences:

```
// Email verification with OTP using verification template
$result = $notificationService->send(
    'email_verification',
    $notifiable,
    'Verify your email address',
    [
        'otp' => '123456',
        'expiry_minutes' => 15,
        'template_name' => 'verification'
    ],
    ['channels' => ['email']]
);

// Password reset with OTP using password-reset template
$result = $notificationService->send(
    'password_reset',
    $notifiable,
    'Password Reset Code',
    [
        'name' => $user->getFirstName(),
        'otp' => '654321',
        'expiry_minutes' => 15,
        'template_name' => 'password-reset'
    ],
    ['channels' => ['email']]
);

// Welcome email using welcome template
$result = $notificationService->send(
    'user_welcome',
    $user,
    'Welcome to Our Platform',
    [
        'user_name' => $user->getName(),
        'welcome_message' => 'Thank you for joining us!',
        'get_started_url' => 'https://example.com/onboarding',
        'template_name' => 'welcome'
    ],
    ['channels' => ['email']]
);

// Alert notification using alert template
$result = $notificationService->send(
    'security_alert',
    $user,
    'Security Alert',
    [
        'alert_type' => 'login_from_new_device',
        'message' => 'Your account was accessed from a new device.',
        'location' => 'San Francisco, CA',
        'device' => 'iPhone 13',
        'timestamp' => '2024-06-21 14:30:00',
        'template_name' => 'alert'
    ],
    ['channels' => ['email']]
);
```

**Key Benefits of this Approach:**

- ✅ **Clean and Simple** - Minimal code required for template-based emails
- ✅ **Automatic Global Variables** - Variables like `app_name`, `current_year`, `logo_url` are automatically available
- ✅ **Template Mappings** - Use friendly template names that map to actual template files
- ✅ **No Duplication** - Each piece of data specified only once
- ✅ **Type Safety** - All template variables are validated and type-checked

**Global Variables Available in All Templates:**

```
// These are automatically available without specifying them:
$globalVariables = [
    'app_name' => 'Glueful Application',
    'app_url' => 'https://example.com',
    'support_email' => 'support@example.com',
    'logo_url' => 'https://brand.glueful.com/logo.png',
    'current_year' => '2024',
    'company_name' => 'Your Company'
];
```

### Advanced Email Features

[](#advanced-email-features)

Leverage Symfony Mailer's advanced capabilities:

```
// Email with attachments, custom headers, and advanced features
$notificationService->send(
    'invoice_generated',
    $customer,
    'Your Invoice #' . $invoice->number,
    [
        'invoice_number' => $invoice->number,
        'amount' => $invoice->total,
        'due_date' => $invoice->due_date,
        'embedImages' => [
            'logo' => '/path/to/logo.png',
        ],
    ],
    [
        'channels' => ['email'],
        'email_options' => [
            'attachments' => [
                [
                    'path' => $invoice->pdf_path,
                    'name' => 'Invoice-' . $invoice->number . '.pdf',
                    'contentType' => 'application/pdf'
                ]
            ],
            'cc' => ['accounting@example.com'],
            'bcc' => ['archive@example.com'],
            'priority' => 'high',
            'headers' => [
                'X-Invoice-ID' => $invoice->id,
                'X-Customer-ID' => $customer->id,
            ],
            'returnPath' => 'bounces@example.com',
        ]
    ]
);
```

Asynchronous Delivery
---------------------

[](#asynchronous-delivery)

This extension does not manage its own queue. Whether emails are sent synchronously or queued is decided by the framework's notification dispatcher — push notifications onto the framework queue at the dispatch layer and run workers (`php glueful queue:work`) per the framework's queue documentation. The channel itself sends a single message when invoked.

### Retry tuning

[](#retry-tuning)

Delivery-failure retry behavior is configured under `emailnotification.retry` (env `MAIL_RETRY_ENABLED`, `MAIL_RETRY_MAX_ATTEMPTS`, `MAIL_RETRY_DELAY`, `MAIL_RETRY_BACKOFF`, `MAIL_RETRY_JITTER`). On boot the extension surfaces this under the framework's channel-agnostic `notifications.retry` key (Framework 1.51.0+), so the core retry service picks it up. A transport failure returns a retryable `transport_exception` result; configuration errors return the non-retryable `transport_misconfigured` result and are not retried.

Transport Features
------------------

[](#transport-features)

### Multi-Transport Support

[](#multi-transport-support)

Configure failover so a send falls through to the next mailer when one is unavailable:

```
// Failover configuration
'failover' => [
    'mailers' => ['brevo', 'sendgrid', 'smtp'],
],
```

### Transport Health Monitoring

[](#transport-health-monitoring)

```
use Glueful\Extensions\EmailNotification\TransportFactory;

// Check available providers
$providers = TransportFactory::getAvailableProviders();
// Returns status of all Symfony provider bridges

// Verify transport configuration
$emailChannel->isAvailable(); // Returns true if transport is configured
```

Template System
---------------

[](#template-system)

The extension provides a flexible template system with built-in responsive templates and support for custom templates.

### Built-in Templates

[](#built-in-templates)

The extension includes 6 professionally designed, responsive email templates:

#### 1. Default Template (`default.html`)

[](#1-default-template-defaulthtml)

- **Use Case**: General notifications, alerts, and multi-purpose emails
- **Features**: OTP support, action buttons, customizable styling
- **Variables**: `{{subject}}`, `{{message}}`, `{{action_url}}`, `{{action_text}}`, `{{otp_code}}`

#### 2. Welcome Template (`welcome.html`)

[](#2-welcome-template-welcomehtml)

- **Use Case**: User onboarding and welcome emails
- **Features**: Friendly greeting, getting started guidance
- **Variables**: `{{user_name}}`, `{{app_name}}`, `{{welcome_message}}`, `{{get_started_url}}`

#### 3. Alert Template (`alert.html`)

[](#3-alert-template-alerthtml)

- **Use Case**: Security alerts, important notifications, warnings
- **Features**: Attention-grabbing design, urgency indicators
- **Variables**: `{{alert_type}}`, `{{message}}`, `{{timestamp}}`, `{{action_required}}`

#### 4. Password Reset Template (`password-reset.html`)

[](#4-password-reset-template-password-resethtml)

- **Use Case**: Password reset functionality
- **Features**: Secure reset process, expiry warnings
- **Variables**: `{{user_name}}`, `{{reset_url}}`, `{{expiry_time}}`, `{{security_tip}}`

#### 5. Verification Template (`verification.html`)

[](#5-verification-template-verificationhtml)

- **Use Case**: Account verification, email confirmation
- **Features**: Verification codes, confirmation links
- **Variables**: `{{user_name}}`, `{{verification_code}}`, `{{verification_url}}`, `{{expiry_time}}`

#### 6. Two-Factor PIN Template (`two-factor-pin.html`)

[](#6-two-factor-pin-template-two-factor-pinhtml)

- **Use Case**: Two-factor authentication one-time codes
- **Features**: Prominent PIN display, expiry warning
- **Variables**: `{{user_name}}`, `{{otp}}`, `{{expiry_minutes}}`

### Custom Templates

[](#custom-templates)

You can add your own email templates and customize the template system through configuration.

#### Template Configuration

[](#template-configuration)

Configure custom templates in `config/services.php`:

```
'mail' => [
    'templates' => [
        // Primary template directory (optional override). By default, the
        // extension's own templates are used. To override explicitly:
        // 'path' => base_path('vendor/glueful/email-notification/src/Templates/html'),

        // Additional custom template directories (checked in order)
        'custom_paths' => [
            // Framework's mail templates
            dirname(__DIR__) . '/resources/mail',
            // Your custom templates directory
            dirname(__DIR__) . '/templates/email',
        ],

        // Layout and partials
        'default_layout' => env('MAIL_DEFAULT_LAYOUT', 'layout'),
        'partials_directory' => 'partials',

        // Template file extension
        'extension' => '.html',

        // Custom template mappings (aliases)
        'mappings' => [
            // Map friendly names to actual template files
            'user_welcome' => 'onboarding/welcome',
            'password_reset' => 'auth/reset-password',
            'invoice' => 'billing/invoice-generated',
        ],

        // Global variables available to all templates
        'global_variables' => [
            'app_name' => env('APP_NAME', 'Glueful Application'),
            'app_url' => env('BASE_URL', 'https://example.com'),
            'support_email' => env('MAIL_SUPPORT_EMAIL', 'support@example.com'),
            'logo_url' => env('MAIL_LOGO_URL', 'https://brand.glueful.com/logo.png'),
            'current_year' => date('Y'),
            'company_name' => env('COMPANY_NAME', 'Your Company'),
        ],
    ],
],
```

#### Creating Custom Templates

[](#creating-custom-templates)

1. **Create Template Directory Structure**: ```
    resources/mail/
    ├── custom-welcome.html
    ├── invoice.html
    ├── newsletter.html
    └── partials/
        ├── layout.html
        ├── header.html
        └── footer.html

    ```

> **Override behavior**: if you set `services.mail.templates.custom_paths`, the extension will load templates from those paths first. Only templates or partials you provide are overridden; everything else falls back to the built‑in templates.

2. **Custom Template Example** (`resources/mail/invoice.html`):

    ```
    >

        {{subject}}

            .invoice-header { background: #f8f9fa; padding: 20px; }
            .invoice-details { margin: 20px 0; }
            .total { font-weight: bold; font-size: 18px; }

            {{app_name}}
            Invoice #{{invoice_number}}

            Dear {{customer_name}},
            Your invoice is ready for review.

                Invoice Number:{{invoice_number}}
                Amount:${{amount}}
                Due Date:{{due_date}}

            View Invoice Online

        {{> footer}}

    ```
3. **Using Custom Templates**:

    ```
    // Use by filename
    $notificationService->sendWithTemplate(
        'invoice_generated',
        $customer,
        'invoice', // Uses resources/mail/invoice.html
        [
            'invoice_number' => 'INV-2024-001',
            'customer_name' => $customer->name,
            'amount' => '299.99',
            'due_date' => '2024-07-15',
            'invoice_url' => 'https://app.com/invoices/123',
        ]
    );

    // Use with mapping alias
    $notificationService->sendWithTemplate(
        'user_registration',
        $user,
        'user_welcome', // Maps to onboarding/welcome.html via template mappings
        [
            'user_name' => $user->name,
            'activation_url' => $activationUrl,
        ]
    );
    ```

#### Template Features

[](#template-features)

**Variable Substitution**:

- Simple variables: `{{variable_name}}`
- Default values: `{{variable_name|default_value}}`
- Nested variables: `{{user.profile.name}}`

> **Auto-escaping (security):** every `{{variable}}` (and its default literal) is HTML-escaped with `htmlspecialchars(ENT_QUOTES | ENT_HTML5)`, so notification data (display names, messages) cannot inject markup into outgoing mail. For slots that intentionally receive pre-rendered HTML, use the **raw** triple-mustache `{{{variable}}}` — the shipped layout's `{{{content}}}` is the only such slot; only use it for values you fully control. The `action_url`/`reset_url` values are blanked unless their scheme is `http`/`https` (relative URLs pass; `javascript:`/`data:` and malformed URLs are rejected).

**Conditional Blocks**:

```
{{#if show_discount}}

    Special offer: {{discount_percent}}% off!

{{/if}}
```

**Partials (Template Includes)**:

```
{{> header}}

{{> footer}}
```

**Global Variables**: All templates automatically have access to configured global variables like `{{app_name}}`, `{{logo_url}}`, `{{current_year}}`, etc.

#### Template Inheritance

[](#template-inheritance)

Create a base layout in `partials/layout.html`:

```
>

    {{subject}} - {{app_name}}

        /* Your base styles */

        {{{content}}}

        &copy; {{current_year}} {{company_name}}. All rights reserved.

```

Templates without `` automatically use this layout.

Security Features
-----------------

[](#security-features)

### Recipient Domain Policy

[](#recipient-domain-policy)

`EmailChannel` enforces an optional recipient-domain policy **before** sending. A recipient whose domain is disallowed yields a non-retryable `NotificationResult` failure (`blocked_domain`) and no mail is sent.

```
# Denylist: a matching recipient domain is rejected (comma-separated)
MAIL_BLOCKED_DOMAINS=example.com,spam.test

# Allowlist: when set, ONLY matching recipient domains are permitted (comma-separated)
MAIL_ALLOWED_DOMAINS=yourcompany.com,partner.com
```

With neither set, all recipient domains are allowed. Both also accept an array in `config/emailnotification.php` under `security.allowed_domains` / `security.blocked_domains`.

The policy is enforced on **every** recipient — the primary address plus all cc/bcc entries — so an allowlist cannot be bypassed via a cc/bcc field; any disallowed (or non-string) entry fails the whole send closed. Matching is **asymmetric by design**: the blocklist also matches subdomains (blocking `evil.com` blocks `sub.evil.com`), while the allowlist is **exact-match**only (allowlisting `company.com` does **not** permit `sub.company.com`).

### Attachment Path Confinement

[](#attachment-path-confinement)

Attachment and embedded-image paths come from notification data, so they are confined to allowed base directories before reaching Symfony: a path is accepted only when `realpath()`resolves it inside an allowed base (sibling-dir-safe — `/app/storage-evil` cannot pass for `/app/storage`). Configure via `security.attachment_allowed_paths` (array) in `config/emailnotification.php`; when null/empty it defaults to the application storage directory. A rejected path is a loud, non-retryable `invalid_attachment` failure with an ERROR log naming the path — never a silent skip.

### Transport security

[](#transport-security)

- **Symfony Mailer**: transport built on Symfony's mailer security.
- **Provider isolation**: isolated transport creation per send.
- **Fail-loud misconfiguration**: a missing SMTP host, missing provider-bridge credentials, or any transport-factory failure is a non-retryable `transport_misconfigured` failure (ERROR log, config keys only) — the channel never silently falls back to a null sink that would report success while discarding mail. An explicitly configured null sink (`transport: 'null'` or a `null://` DSN) remains supported for development.
- **Error sanitization**: send failures return a structured `NotificationResult` (error code + message) and never throw SMTP credentials into the dispatcher. Logs never carry payload values (OTP pins, reset tokens/URLs, PII) — only payload keys plus the `subject`/`type`/`template_name`identifiers; diagnostics (`getExtensionInfo()`) return a credential-free config summary.

Monitoring and Debugging
------------------------

[](#monitoring-and-debugging)

### Health &amp; Metrics

[](#health--metrics)

```
use Glueful\Extensions\EmailNotification\EmailNotificationProvider;

$provider = app($context, EmailNotificationProvider::class);

// Check provider configuration status
$isConfigured = $provider->isEmailProviderConfigured();
```

Delivery metrics (per-channel delivery times, retry counts and distributions) are owned by the framework's notification system — use `NotificationService::getMetricsService()`(`Glueful\Notifications\Services\NotificationMetricsService`), which is fed by the structured `NotificationResult` this channel returns for every send.

### Debug Mode

[](#debug-mode)

Enable detailed logging for troubleshooting:

```
MAIL_DEBUG=true          # debug-log each outgoing email (recipient/subject/type only)
MAIL_LOG_RESULTS=true    # info/error-log each send outcome
APP_DEBUG=true
```

Migration from PHPMailer
------------------------

[](#migration-from-phpmailer)

### Breaking Changes in v1.0.0

[](#breaking-changes-in-v100)

1. **Transport Configuration**: New multi-mailer structure required
2. **Queue System**: File-based queue replaced with framework queue
3. **Provider Specification**: Explicit transport types required (e.g., `brevo+api`)
4. **Dependencies**: Symfony Mailer replaces PHPMailer

### Migration Steps

[](#migration-steps)

1. **Update Dependencies**:

    ```
    composer require symfony/mailer symfony/brevo-mailer
    ```
2. **Update Configuration**:

    ```
    // OLD (PHPMailer)
    'mail' => [
        'host' => 'smtp.brevo.com',
        'port' => 587,
        'username' => 'user@domain.com',
        'password' => 'password',
    ]

    // NEW (Symfony Mailer)
    'mail' => [
        'default' => 'brevo',
        'mailers' => [
            'brevo' => [
                'transport' => 'brevo+smtp',
                'username' => env('MAIL_USERNAME'),
                'password' => env('MAIL_PASSWORD'),
            ],
        ],
    ]
    ```
3. **Update Environment Variables**:

    ```
    MAIL_MAILER=brevo
    BREVO_TRANSPORT=brevo+smtp  # or brevo+api
    MAIL_ENCRYPTION=tls         # not MAIL_SECURE
    ```
4. **Verify Configuration**:

    ```
    # Confirm the extension is discovered/enabled and view its metadata
    php glueful extensions:info email-notification
    ```

Troubleshooting
---------------

[](#troubleshooting)

### Common Issues

[](#common-issues)

1. **Transport Creation Errors**

    - Verify provider bridge is installed: `composer require symfony/brevo-mailer`
    - Check configuration structure in `services.php`
    - Review error logs for specific transport issues
2. **Emails Not Sending Asynchronously**

    - Async delivery is governed by the framework's notification dispatcher/queue, not this extension — see the framework queue documentation
    - Start queue workers: `php glueful queue:work`
3. **Provider Bridge Issues**

    - Verify API credentials are correct
    - Check transport specification (e.g., `brevo+api` vs `brevo+smtp`)
    - Review provider-specific documentation
4. **Configuration Path Issues**

    - Ensure the provider is discovered (composer-installed) and, if needed, enabled in `config/extensions.php`
    - Verify `services.mail` configuration exists
    - Check `from` address is configured

### Health Checks

[](#health-checks)

This extension registers no HTTP routes. Diagnose configuration from PHP/CLI instead:

```
$provider = app()->get(\Glueful\Extensions\EmailNotification\EmailNotificationProvider::class);
$provider->isEmailProviderConfigured();   // bool — credentials/transport/from validated
$provider->getExtensionInfo();            // credential-free config summary + feature flags
```

```
php glueful extensions:info email-notification
```

Provider-Specific Setup
-----------------------

[](#provider-specific-setup)

### Brevo (Sendinblue)

[](#brevo-sendinblue)

```
MAIL_MAILER=brevo
BREVO_TRANSPORT=brevo+api           # API mode (recommended)
# BREVO_TRANSPORT=brevo+smtp        # SMTP mode
BREVO_API_KEY=your-brevo-api-key
MAIL_USERNAME=your-smtp-login
MAIL_PASSWORD=your-smtp-key
```

### SendGrid

[](#sendgrid)

```
MAIL_MAILER=sendgrid
SENDGRID_API_KEY=your-sendgrid-key
```

### Amazon SES

[](#amazon-ses)

```
MAIL_MAILER=ses
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_DEFAULT_REGION=us-east-1
```

### Mailgun

[](#mailgun)

```
MAIL_MAILER=mailgun
MAILGUN_DOMAIN=your-domain.mailgun.org
MAILGUN_SECRET=your-mailgun-key
MAILGUN_REGION=us                  # or eu
```

License
-------

[](#license)

This extension is licensed under the MIT License.

Support
-------

[](#support)

For issues, feature requests, or questions about the EmailNotification extension:

- Create an issue in the repository
- Consult the [Symfony Mailer documentation](https://symfony.com/doc/current/mailer.html)
- Check the extension health monitoring for diagnostics

---

**📚 Documentation**: [Glueful Framework Documentation](https://docs.glueful.com)
**🔧 Provider Bridges**: [Symfony Mailer Bridges](https://symfony.com/doc/current/mailer.html#using-a-3rd-party-transport)

###  Health Score

44

—

FairBetter than 90% of packages

Maintenance95

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity58

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~21 days

Recently: every ~4 days

Total

14

Last Release

20d ago

PHP version history (2 changes)v1.0.0PHP ^8.2

v1.2.0PHP ^8.3

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/95650726?v=4)[msowah](/maintainers/msowah)[@msowah](https://github.com/msowah)

---

Top Contributors

[![MichaelSowah](https://avatars.githubusercontent.com/u/24699905?v=4)](https://github.com/MichaelSowah "MichaelSowah (51 commits)")

---

Tags

emailnotificationssmtpcommunication

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP\_CodeSniffer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/glueful-email-notification/health.svg)

```
[![Health](https://phpackages.com/badges/glueful-email-notification/health.svg)](https://phpackages.com/packages/glueful-email-notification)
```

###  Alternatives

[laravel/framework

The Laravel Framework.

34.8k543.8M20.1k](/packages/laravel-framework)[tempest/framework

The PHP framework that gets out of your way.

2.2k34.4k15](/packages/tempest-framework)[drupal/core

Drupal is an open source content management platform powering millions of websites and applications.

21866.0M1.7k](/packages/drupal-core)[drupal/core-recommended

Locked core dependencies; require this project INSTEAD OF drupal/core.

6942.5M421](/packages/drupal-core-recommended)[kimai/kimai

Kimai - Time Tracking

4.8k9.0k1](/packages/kimai-kimai)[shopware/core

Shopware platform is the core for all Shopware ecommerce products.

585.6M574](/packages/shopware-core)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
