Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Notifications Bundle Laravel Package

mrad/notifications-bundle

A PHP notifications bundle that provides a reusable notification layer for your application, with support for multiple delivery channels, templated messages, and a clean API for triggering alerts from domain logic. Ideal for adding user-facing notifications without rebuilding delivery infrastructure...

View on GitHub
Deep Wiki
Context7

Getting Started

Before writing a line, confirm the framework mismatch. mrad/notifications-bundle is a Symfony bundle (type: symfony-bundle, DependencyInjection/*Extension.php, Resources/config/services.yml), not a Laravel package. There is no ServiceProvider, no config/notifications.php to publish, and Composer auto-discovery will do nothing. Do not composer require it into a Laravel app and expect wiring — you will get either a resolution failure on symfony/framework-bundle or a stale Pusher SDK that breaks on PHP 8.

Read these four files in order (roughly 30 minutes total):

  1. composer.json — confirms the Symfony constraint and the pinned pusher/pusher-php-server version (expect v2/v3 era).
  2. DependencyInjection/Configuration.php — the actual config keys you'll mirror (app_id, key, secret, cluster, sometimes encrypted).
  3. Resources/config/services.yml — the service graph, which reveals the one class that does real work.
  4. That single Pusher wrapper class — the only part with day-to-day value.

Your first use case should be the smallest thing that proves the wiring: a queued job that triggers a server-side event to a per-user private channel when a domain event fires (order shipped, invoice paid). Skip presence channels and client-side auth for now — the bundle gives you the least help there and you'll be writing that code yourself anyway.

Concretely, your onboarding is:

composer require pusher/pusher-php-server
php artisan make:provider NotificationServiceProvider
php artisan make:class Services/PusherNotifier

Then bind App\Contracts\Notifier to PusherNotifier in the provider, pull credentials from .env (PUSHER_APP_ID, PUSHER_APP_KEY, PUSHER_APP_SECRET, PUSHER_APP_CLUSTER), and mirror the bundle's payload shape only if a client already depends on it.

Implementation Patterns

Wrap, don't inherit. The DI wiring is Symfony-specific and useless to you; the trigger logic is portable. Extract the $pusher->trigger($channel, $event, $payload) call into your own service behind an interface, so the transport is replaceable later (Reverb, Ably, Mercure, SNS).

interface Notifier {
    public function notify(int|string $userId, string $event, array $payload): void;
}

Queue the dispatch. Never call trigger() inline from a controller. Wrap it in a ShouldQueue job so a Pusher timeout doesn't block the request, and so you get retries ($tries, $backoff) the original bundle doesn't offer.

class SendNotificationJob implements ShouldQueue {
    use Queueable, SerializesModels;
    public int $tries = 3;
    public function __construct(
        public int $userId,
        public string $event,
        public array $payload,
    ) {}
    public function handle(Notifier $notifier): void {
        $notifier->notify($this->userId, $this->event, $this->payload);
    }
}

Event listener → notification. Keep domain code decoupled, which is the one design idea the bundle gets right. Register a listener on your domain event, dispatch the job from there. Domain code never imports Pusher.

Channel naming convention. Standardise early: private-user.{id} for per-user, private-org.{id} for tenant-wide. The bundle has no opinion on this; you'll live with whatever you pick, so pick once.

Payload shape. If you're porting from an existing client that consumed this bundle's payloads, preserve { title, body, icon, url } or whatever the bundle emits — read Resources/views/*.twig (the bundle renders message bodies via Twig, so the shape is discoverable there). Otherwise design fresh; there is no compatibility constraint.

Testing seam. Bind a FakeNotifier in your test service provider that records calls, and assert against it. This is trivial with the interface and impossible with the bundle's concrete service.

If you actually want Laravel-native broadcasting (recommended long-term), skip this package entirely: BROADCAST_DRIVER=pusher plus php artisan install:broadcasting gives you queueing, multiple channels, and Echo client-side, maintained by the framework.

Gotchas and Tips

Symfony bundle ≠ Laravel package. This is the biggest trap. If someone hands you this repo as a "Laravel notification package," correct the premise before you burn a day. Check composer.json type and require first — that resolves the question in 10 seconds.

SDK drift. The bundle pins an old pusher/pusher-php-server (v2/v3). That SDK predates current Channels auth, TLS defaults, and PHP 8 type semantics. Don't inherit its constraint — use the current SDK and adapt the wrapper's call signature (the trigger() API has been stable enough that this is usually a copy-paste).

Cluster config is mandatory. The Pusher client requires cluster; omitting it produces a cryptic connection error at trigger time, not at boot. Put it in .env and validate at provider boot.

Private channel auth. The bundle does server-side trigger only. Private/presence channels require the client to hit an auth endpoint on your app — you author /broadcasting/auth (or a custom route) and sign with the secret. The bundle gives you zero help here; use Laravel's Broadcast::auth() if you go native.

Fire-and-forget is the default failure mode. These bundles typically call trigger() with no return check. Silent notification loss is normal, not exceptional. Wrap the call, check the returned response, log failures with Log::warning, and emit a metric. Add a retry path via the queue — retrofitting delivery guarantees after notifications are load-bearing is far more expensive than doing it now.

Credential hygiene. Read the wrapper before any secret touches it — single-maintainer packages of this vintage sometimes inline config or log connection details. Rotate your Pusher secret after the spike regardless.

Config caching pitfall. If you do bridge it and read config via env() directly inside a service, php artisan config:cache will return null. Always read via config('services.pusher.*') and define the mapping in config/services.php.

Don't fork the repo. A fork converts a zero-cost read into a permanent maintenance liability with no upstream to rebase against. Port what you need into app/ and keep the license attribution in a comment.

Ramp-up cost is trust, not comprehension. The code is small and trivially readable. The time sink is justifying to yourself (and reviewers) why an unmaintained, zero-dependent wrapper routes production credentials. That conversation reliably takes longer than reimplementing it in an afternoon — plan for that, and have the native solution ready as the fallback answer.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings