meanify-co/laravel-notifications
Composer 安装命令:
composer require meanify-co/laravel-notifications
包简介
A PHP library to handle multichannel notifications in Laravel
README 文档
README
Laravel Notifications
A complete library for centralized multichannel notifications in Laravel, supporting multiple email drivers, real-time broadcasting, dynamic templates with multi-language support, and recipient filtering.
Table of Contents
- Features
- Requirements
- Installation
- Configuration
- Database Setup
- Usage
- Template System
- Recipient Filter
- Notification Statuses
- Artisan Commands
- Models Reference
- License
Features
- Multiple email drivers: SMTP, Mailgun, SendGrid, SendPulse
- Email attachments: file path or base64-encoded content
- Real-time broadcasting: in-app notifications via Laravel Broadcasting
- Custom broadcast channels: obfuscated model-based channels or simple string channels
- Multi-language: locale-based template translations (e.g.
pt_BR,en_US) - Dynamic templates: flexible interpolation engine with conditionals, loops, and isset blocks
- Queue processing: async dispatch via Laravel queues with retries and backoff
- Immediate sending: bypass queue when needed
- Recipient filtering: block emails by domain patterns, encrypted or base64-encoded addresses
- Payload encryption: sensitive notification data is encrypted at rest
- Preview mode: render notifications without dispatching
- Scheduled delivery: send notifications at a future date/time
Requirements
- PHP >= 8.0
- Laravel 10.x, 11.x, or 12.x
Installation
composer require meanify-co/laravel-notifications
The service provider is auto-discovered. Then publish the assets:
# Publish config php artisan vendor:publish --tag=meanify-configs # Publish models php artisan vendor:publish --tag=meanify-models # Publish migrations php artisan vendor:publish --tag=meanify-migrations # Publish seeders php artisan vendor:publish --tag=meanify-seeders # Run migrations php artisan migrate
Configuration
After publishing, the config file is at config/meanify-laravel-notifications.php.
return [ 'default_queue_name' => 'meanify.queue.notifications', 'user_model' => \App\Models\User::class, 'default_email_layout' => 'default', 'email' => [ 'enabled' => true, 'queue' => 'meanify.queue.notifications.emails', 'tries' => 3, 'backoff' => 30, 'verify_ssl' => false, 'send_immediately' => false, 'recipient_filter' => [ 'enabled' => false, 'blocked_domains' => [], 'block_encrypted' => true, 'block_base64' => true, 'on_block_status' => 'simulated', ], ], 'in_app' => [ 'enabled' => true, 'queue' => 'meanify.queue.notifications.in_app', ], 'broadcast' => [ 'channel_prefix' => 'mfy_channel_', 'enabled' => true, ], ];
Email Channel
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Enable/disable email sending globally |
queue |
string | meanify.queue.notifications.emails |
Queue name for email jobs |
tries |
int | 3 |
Max retry attempts on failure |
backoff |
int | 30 |
Seconds between retries |
verify_ssl |
bool | false |
SSL verification for API drivers |
send_immediately |
bool | false |
If true, bypasses the queue globally |
In-App Channel
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Enable/disable in-app notifications |
queue |
string | meanify.queue.notifications.in_app |
Queue name for broadcast jobs |
Broadcasting
| Key | Type | Default | Description |
|---|---|---|---|
channel_prefix |
string | mfy_channel_ |
Prefix for obfuscated broadcast channels |
enabled |
bool | true |
Enable/disable broadcasting |
Recipient Filter
Allows blocking email delivery based on recipient patterns. See Recipient Filter for details.
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Enable/disable recipient filtering |
blocked_domains |
array | [] |
Domain patterns to block (supports wildcards) |
block_encrypted |
bool | true |
Block emails that appear encrypted |
block_base64 |
bool | true |
Block emails that appear base64-encoded |
on_block_status |
string | simulated |
Status when blocked: simulated or skipped |
Database Setup
Tables Overview
The migration creates the following tables:
emails_layouts
Stores reusable email layout templates (Blade-based).
| Column | Type | Description |
|---|---|---|
id |
bigint | Primary key |
name |
string | Layout display name |
key |
string (unique) | Identifier (e.g. default) |
metadata |
json | Colors, fonts, social links, etc. |
blade_template |
longText | Blade template with {{ $content }} |
notifications_templates
Master notification template definitions.
| Column | Type | Description |
|---|---|---|
id |
bigint | Primary key |
email_layout_id |
foreignId (nullable) | Associated email layout |
key |
string (unique) | Template identifier (e.g. user_registered) |
available_channels |
json | Channels array: ['email', 'in_app'] |
force_channels |
json (nullable) | Override available channels |
active |
boolean | Enable/disable template |
notifications_templates_translations
Localized content for each template.
| Column | Type | Description |
|---|---|---|
notification_template_id |
bigint | Foreign key |
locale |
string(10) | Language code (e.g. pt_BR) |
subject |
string | Email subject |
title |
string | Email title |
body |
text | Email body with template variables |
short_message |
string | In-app notification message |
notifications_templates_variables
Documents expected variables per template.
| Column | Type | Description |
|---|---|---|
notification_template_id |
bigint | Foreign key |
key |
string | Variable name (e.g. user_name) |
description |
text | Human description |
example |
string | Example value |
notifications
Log of all dispatched notifications.
| Column | Type | Description |
|---|---|---|
notification_template_id |
foreignId (nullable) | Template reference |
user_id |
bigint (nullable) | Recipient user |
application_id |
bigint (nullable) | Application context |
account_id |
bigint (nullable) | Account context |
session_id |
bigint (nullable) | Session context |
channel |
string | email or in_app |
payload |
json (encrypted) | Full notification data |
status |
enum | pending, processing, sent, failed, simulated, skipped |
scheduled_to |
timestamp (nullable) | Scheduled delivery time |
sent_at |
timestamp (nullable) | Actual send time |
failed_at |
timestamp (nullable) | Failure time |
failed_log |
json (nullable) | Exception details |
Note: The
application_id,account_id, andsession_idforeign keys are conditionally added only if their corresponding tables exist in the database.
Seeders
After publishing, run the seeders to populate default data:
# Seeds the default email layout php artisan db:seed --class=EmailLayoutSeeder # Seeds pre-built notification templates (pt_BR + en_US) php artisan db:seed --class=NotificationTemplateSeeder
Included templates: sign_in_otp, forgot_password_otp, new_login_detected, email_change_verification, password_changed_notice, email_changed_notice, invite_new_user, in_app_only_1, combined_notification.
Usage
NotificationBuilder (Fluent API)
The NotificationBuilder is the primary interface for creating and sending notifications.
use Meanify\LaravelNotifications\Support\NotificationBuilder; NotificationBuilder::make('template_key', $user, 'pt_BR') ->onAccount($accountId) // optional: account context ->onApplication($applicationId) // optional: application context ->onSession($sessionId) // optional: session context ->forEmail('smtp', $configs, $recipients) ->with(['user_name' => 'John']) // template variables ->withAttachments([...]) // optional: file attachments ->toBroadcastChannels([...]) // optional: custom broadcast channels ->scheduledTo(now()->addHours(1)) // optional: delay delivery ->send(); // dispatch!
Available Methods
| Method | Description |
|---|---|
make($templateKey, $user, $locale) |
Static factory. Creates a builder instance. |
onAccount($id) |
Sets account context. |
onApplication($id) |
Sets application context. |
onSession($id) |
Sets session context. |
forEmail($driver, $configs, $recipients, $sendImmediately) |
Configures email delivery. |
with($data) |
Sets dynamic template variables. |
setSubject($subject) |
Overrides the email subject. |
withRenderedHtml($html, $subject, $payload) |
Uses pre-rendered HTML instead of template. |
withAttachments($attachments) |
Adds file attachments. |
toBroadcastChannels($channels) |
Sets custom broadcast channels for in-app. |
scheduledTo($carbon) |
Schedules delivery for a future time. |
preview($interpolate) |
Renders the notification without sending. Returns array. |
send() |
Dispatches the notification. Returns bool. |
Helper Function
A global helper is available for convenience:
// With arguments: returns NotificationBuilder meanify_notifications('template_key', $user, 'pt_BR') ->forEmail('smtp', $configs, ['user@example.com']) ->with(['code' => '123456']) ->send(); // Without arguments: returns NotificationUtils $email = meanify_notifications()->decryptRecipientToUnsubscribe($encryptedToken);
Sending Emails
NotificationBuilder::make('welcome_email', $user, 'pt_BR') ->forEmail('smtp', [ 'smtp_host' => 'smtp.example.com', 'smtp_port' => 587, 'smtp_encryption' => 'tls', 'smtp_username' => 'notifications@example.com', 'smtp_password' => 'your-encrypted-password', 'from_address' => 'noreply@example.com', 'from_name' => 'My App', ], ['recipient@example.com']) ->with([ 'user_name' => 'John Doe', 'activation_link' => 'https://example.com/activate/abc123', ]) ->send();
To send immediately (bypassing queue):
NotificationBuilder::make('otp_email', $user, 'en_US') ->forEmail('smtp', $configs, ['user@example.com'], sendImmediately: true) ->with(['otp' => '123456']) ->send();
Email Drivers
SMTP
->forEmail('smtp', [ 'smtp_host' => 'smtp.example.com', 'smtp_port' => 587, 'smtp_encryption' => 'tls', 'smtp_username' => 'user@example.com', 'smtp_password' => 'encrypted-password', 'from_address' => 'noreply@example.com', 'from_name' => 'App Name', ], $recipients)
Mailgun
->forEmail('mailgun', [ 'mailgun_domain' => 'mg.example.com', 'mailgun_api_key' => 'key-xxx', 'mailgun_endpoint' => 'https://api.mailgun.net', 'from_address' => 'noreply@example.com', 'from_name' => 'App Name', ], $recipients)
SendGrid
->forEmail('sendgrid', [ 'sendgrid_api_key' => 'SG.xxx', 'sendgrid_endpoint' => 'https://api.sendgrid.com', 'from_address' => 'noreply@example.com', 'from_name' => 'App Name', ], $recipients)
SendPulse
->forEmail('sendpulse', [ 'sendpulse_client_id' => 'client-id', 'sendpulse_client_secret' => 'client-secret', 'sendpulse_endpoint' => 'https://api.sendpulse.com', 'from_address' => 'noreply@example.com', 'from_name' => 'App Name', ], $recipients)
Attachments
Supports two formats: file path and base64 content.
NotificationBuilder::make('invoice_email', $user, 'pt_BR') ->forEmail('smtp', $configs, ['finance@example.com']) ->with(['invoice_number' => 'INV-001']) ->withAttachments([ // From file path [ 'path' => '/storage/invoices/inv-001.pdf', 'name' => 'Invoice-001.pdf', 'mime' => 'application/pdf', ], // From base64 content [ 'content' => base64_encode($fileData), 'name' => 'Report.xlsx', 'mime' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ], ]) ->send();
In-App Notifications & Broadcasting
Templates with in_app in available_channels will automatically dispatch broadcast events.
// Simple string channels NotificationBuilder::make('task_assigned', $user, 'pt_BR') ->with(['task' => 'Review PR #42']) ->toBroadcastChannels([ 'user.123', 'admin.dashboard', ]) ->send(); // Model-based obfuscated channels NotificationBuilder::make('project_update', $user, 'pt_BR') ->toBroadcastChannels([ ['model' => User::class, 'id' => $manager->id], ['model' => Team::class, 'id' => $team->id], ]) ->send(); // Channels with custom event names NotificationBuilder::make('alert', $user, 'en_US') ->toBroadcastChannels([ ['channel' => 'project.alerts', 'event' => 'project.completed'], ]) ->send();
Broadcast payload structure:
{
"id": 1,
"type": "task_assigned",
"short_message": "You were assigned to Review PR #42",
"created_at": "2025-01-15T10:30:00Z"
}
Default event name: in-app.notification (customizable per channel).
Scheduling
use Carbon\Carbon; NotificationBuilder::make('weekly_report', $user, 'pt_BR') ->forEmail('sendgrid', $configs, ['user@example.com']) ->with(['report_url' => 'https://...']) ->scheduledTo(Carbon::now()->addHours(2)) ->send();
Preview (Without Sending)
Render the notification HTML without dispatching:
// With template interpolation $preview = NotificationBuilder::make('welcome_email', $user, 'pt_BR') ->with(['user_name' => 'John', 'code' => '123456']) ->preview(); echo $preview['subject']; // Rendered subject echo $preview['html']; // Final HTML with layout // Without interpolation (raw placeholders preserved) $raw = NotificationBuilder::make('welcome_email', $user, 'pt_BR') ->preview(interpolate: false); echo $raw['body']; // "Hello {{ user_name }}, your code is {{ code }}"
Return format:
[
'subject' => 'Rendered subject',
'title' => 'Rendered title',
'body' => 'Rendered body content',
'html' => 'Complete HTML with email layout',
]
Pre-rendered HTML
Send custom HTML without using a template:
NotificationBuilder::make(null, $user, 'pt_BR') ->withRenderedHtml('<h1>Custom Email</h1><p>Hello!</p>', 'Custom Subject') ->forEmail('smtp', $configs, ['user@example.com']) ->send();
Setting Subject Override
Override the template's subject:
NotificationBuilder::make('generic_email', $user, 'pt_BR') ->setSubject('Urgent: Action Required') ->forEmail('smtp', $configs, ['user@example.com']) ->with(['message' => 'Please review immediately.']) ->send();
Template System
Template Interpolation Syntax
The TemplateInterpolator provides a lightweight template engine for notification content.
Variable Replacement
Hello {{ user_name }}, your code is {{ otp }}.
Conditionals
@if(is_premium)
Welcome, premium member!
@else
Upgrade to premium for more features.
@endif
Supported expressions:
- Simple:
@if(variable_name) - Negation:
@if(!variable_name) isset():@if(isset(optional_field))empty():@if(empty(comments))count():@if(count(items) > 0)- Logical
&&/||:@if(active && verified) - Combined:
@if((is_admin || is_moderator) && !banned)
Isset Blocks
@isset(discount_code)
Use code {{ discount_code }} for 10% off!
@endisset
Foreach Loops
Your recent orders:
@foreach(orders as $order)
- Order #{{ $order.id }}: {{ $order.total }}
@endforeach
Both {{ $item.key }} and {{ $item->key }} syntaxes are supported.
Complete Template Example
Hello {{ user_name }},
@if(has_orders)
Here are your recent orders:
@foreach(orders as $order)
- #{{ $order.id }}: ${{ $order.total }}
@endforeach
@else
You have no orders yet. Start shopping!
@endif
@isset(coupon_code)
Special offer: use code {{ coupon_code }} for a discount!
@endisset
Email Layouts
Layouts are Blade templates stored in the emails_layouts table. They wrap the notification body.
Available variables in layouts:
| Variable | Description |
|---|---|
$content |
Rendered email body |
$subject |
Email subject |
| Metadata keys | All keys from layout metadata JSON (e.g. $logo_url, $help_text) |
| Dynamic data | All keys from dynamic_data (template variables) |
Layout template example:
<html> <body> <header> <img src="{{ $logo_url }}" width="{{ $logo_width }}" /> </header> <main> {!! $content !!} </main> <footer> <p>{{ $help_text }}</p> <a href="{{ $privacy_url }}">{{ $privacy_text }}</a> </footer> </body> </html>
Environment variables for default layout seeder:
| Variable | Description |
|---|---|
APP_EMAIL_SRC |
Logo URL |
APP_EMAIL_WIDTH |
Logo width |
APP_EMAIL_HEIGHT |
Logo height |
APP_EMAIL_SHORT_CTA |
Button text (default: "Click here") |
APP_EMAIL_CTA_HELP |
Help text under CTA |
APP_EMAIL_HELP_TEXT |
Footer text |
APP_EMAIL_SOCIAL_LINK_* |
Social media URLs |
APP_EMAIL_PRIVACY_URL |
Privacy policy link |
APP_EMAIL_UNSUBSCRIBE_URL |
Unsubscribe link |
Recipient Filter
The recipient filter prevents email delivery to certain addresses. When enabled, it evaluates each recipient before sending.
Configuration
'email' => [ 'recipient_filter' => [ 'enabled' => true, 'blocked_domains' => ['example.com', 'mailinator.com', '*.test', 'testenv.*'], 'block_encrypted' => true, 'block_base64' => true, 'on_block_status' => 'simulated', // or 'skipped' ], ],
How It Works
Each recipient is checked in order:
- Encrypted detection — blocks if the email has no
@, starts witheyJ(JWT-like), or has a high ratio of non-alphanumeric characters. - Base64 detection — blocks if the local part (before
@) is a valid base64 string (8+ chars that decode and re-encode identically). - Domain matching — blocks if the domain matches any pattern in
blocked_domains. Supports exact match (example.com), suffix wildcard (*.testmatchesanything.test), and prefix wildcard (testenv.*matchestestenv.pwc.com,testenv.anything.io).
Behavior When Blocked
When all recipients are blocked:
on_block_status |
Notification Status | sent_at |
Use Case |
|---|---|---|---|
simulated |
simulated |
Filled (current time) | Testing/staging — simulates successful delivery |
skipped |
skipped |
null |
Production — clearly marks as not executed |
When some recipients are blocked, only the allowed recipients receive the email. Blocked recipients are logged.
Logging
All blocked recipients generate a Log::warning with:
Email notification simulated — recipients blocked by filter
or
Email notification skipped — recipients blocked by filter
Including: notification_id, blocked email list with reasons (blocked_domain, encrypted, base64_encoded).
Notification Statuses
| Status | Description |
|---|---|
pending |
Created, waiting to be dispatched |
processing |
Email job started, sending in progress |
sent |
Successfully delivered |
failed |
Delivery failed (see failed_log for details) |
simulated |
Blocked by recipient filter, saved as if sent |
skipped |
Blocked by recipient filter, marked as not executed |
Status constants are available on the Notification model:
use App\Models\Notification; Notification::NOTIFICATION_STATUS_PENDING; // 'pending' Notification::NOTIFICATION_STATUS_PROCESSING; // 'processing' Notification::NOTIFICATION_STATUS_SENT; // 'sent' Notification::NOTIFICATION_STATUS_FAILED; // 'failed' Notification::NOTIFICATION_STATUS_SIMULATED; // 'simulated' Notification::NOTIFICATION_STATUS_SKIPPED; // 'skipped'
Artisan Commands
Test Notification
Send a test notification from the command line:
php artisan meanify:notifications:test \
--template=welcome_email \
--user=1 \
--locale=pt_BR \
--vars='{"user_name":"John","activation_code":"123456"}'
Options:
| Option | Required | Description |
|---|---|---|
--template |
Yes | Notification template key |
--user |
Yes | User ID of the recipient |
--locale |
No | Language code (default: app.locale) |
--emails |
No | Comma-separated recipient emails (overrides default) |
--smtp |
No | SMTP settings ID from emails_settings table |
--account |
No | Account ID context |
--application |
No | Application ID context |
--session |
No | Session ID context |
--vars |
No | JSON object with template variables |
Examples:
# Basic test php artisan meanify:notifications:test --template=sign_in_otp --user=1 # With custom recipients php artisan meanify:notifications:test \ --template=test_email \ --user=1 \ --emails="test1@example.com,test2@example.com" # With context php artisan meanify:notifications:test \ --template=invite_new_user \ --user=1 \ --account=10 \ --application=20 \ --vars='{"first_name":"John","organization_name":"Acme"}'
Models Reference
All models are published to app/Models/ and can be customized.
| Model | Table | Key Features |
|---|---|---|
Notification |
notifications |
Encrypted payload, status tracking, user/template relations |
NotificationTemplate |
notifications_templates |
SoftDeletes, channel config, layout relation |
NotificationTemplateTranslation |
notifications_templates_translations |
SoftDeletes, locale-based content |
NotificationTemplateVariable |
notifications_templates_variables |
SoftDeletes, variable documentation |
EmailLayout |
emails_layouts |
SoftDeletes, metadata (array), Blade template |
Key Relationships
NotificationTemplate
├── layout() → belongsTo(EmailLayout)
├── translations() → hasMany(NotificationTemplateTranslation)
└── variables() → hasMany(NotificationTemplateVariable)
Notification
├── user() → belongsTo(User)
└── template() → belongsTo(NotificationTemplate)
License
MIT License. See LICENSE for details.
meanify-co/laravel-notifications 适用场景与选型建议
meanify-co/laravel-notifications 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 548 次下载、GitHub Stars 达 0, 最近一次更新时间为 2025 年 04 月 01 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「php」 「helpers」 「email」 「notifications」 「utils」 「laravel」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 meanify-co/laravel-notifications 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 meanify-co/laravel-notifications 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 meanify-co/laravel-notifications 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
HTML and form generation
Falsy helps you manage half-truths with PHP
Extensible library for building notifications and sending them via different delivery channels.
Email+ extends Kirby's email capabilities by adding support for multiple email services using the same Kirby email API.
Laravel contact us form package to send email and save to database
Alfabank REST API integration
统计信息
- 总下载量: 548
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 13
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2025-04-01