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

Laravel Fcm Notification Laravel Package

benwilkins/laravel-fcm-notification

Send Firebase Cloud Messaging push notifications through Laravel's notification system. This package adds an FCM channel with support for device tokens, topics, conditions, data payloads, and custom notification sounds.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and configure

composer require benwilkins/laravel-fcm-notification
php artisan vendor:publish --provider="Benwilkins\FCM\FcmNotificationServiceProvider" --tag="config"

The published config/fcm.php holds a single server_key entry fed by FCM_SERVER_KEY in .env. The service provider auto-registers; there is nothing to add to config/app.php.

Where to look first

  1. config/fcm.php — the only knob you have out of the box.
  2. FcmChannel — the notification channel class ('fcm' in via()). This is where send() loops over recipients and calls the client.
  3. FcmMessage — the fluent builder you return from toFcm(). Read its constants (PRIORITY_HIGH, PRIORITY_NORMAL) and methods (content(), data(), priority(), timeToLive(), collapseKey()).

Make a model addressable

class User extends Authenticatable
{
    use Notifiable;

    public function routeNotificationForFcm()
    {
        return $this->device_token; // string, or array of strings
    }
}

First notification

class OrderShipped extends Notification
{
    public function __construct(public Order $order) {}

    public function via($notifiable) { return ['fcm']; }

    public function toFcm($notifiable)
    {
        return (new FcmMessage)
            ->content([
                'title' => 'Order shipped',
                'body'  => "Order #{$this->order->id} is on its way",
                'sound' => 'default',
            ])
            ->data(['order_id' => (string) $this->order->id])
            ->priority(FcmMessage::PRIORITY_HIGH);
    }
}

$user->notify(new OrderShipped($order));

That order-shipped/status-change push is the canonical first use case: a short, high-priority notification with a small data payload the app can deep-link on.

Implementation Patterns

One notification, many devices. routeNotificationForFcm() may return an array of tokens; the channel iterates and sends one request per token. If you need per-device behaviour, store tokens in a device_tokens table and return the collection.

Topic broadcast. Return a topic address instead of a token:

public function routeNotificationForFcm()
{
    return '/topics/announcements'; // or $this->subscribed_topic
}

This is how you fan out to thousands of users without per-token HTTP calls — subscribe devices client-side with the Firebase SDK, then send once.

Silent / data-only sync. Leave out content() and send only data(). On Android this triggers onMessageReceived in a background handler; on iOS you need content_available in the payload and the app must be trusted. Use it for cache invalidation, not user-visible messaging.

Priority and delivery controls.

->priority(FcmMessage::PRIORITY_HIGH) // wakes sleeping devices; reserve for time-sensitive
->timeToLive(3600)                    // seconds — drop stale chat pings
->collapseKey('chat_'.$threadId)     // replace older notifications in the tray

Queue it. Implement ShouldQueue on the notification (or call notify() from a job). The channel makes a blocking HTTP call per token — never do that inline in a web request that has several tokens.

Testing. Notification::fake() intercepts normally because this is a standard channel; no FCM traffic is generated:

Notification::fake();
$user->notify(new OrderShipped($order));

Notification::assertSentTo($user, OrderShipped::class, function ($n, $channels) {
    return in_array('fcm', $channels);
});

That covers notification routing and payload logic. Actual HTTP serialization is untested — stub the client if you need that.

Reuse across notifications. Extract a BasePushNotification with a toFcm() that reads title, body, and data from abstract methods, so payload shape stays consistent across your app.

Gotchas and Tips

The legacy API is the elephant in the room. This package delegates to brozot/laravel-fcm, which posts to https://fcm.googleapis.com/fcm/send with Authorization: key=<server_key>. Google decommissioned that endpoint and server-key auth in mid-2024. On a new project you will get 401/404 responses no matter how correct your code is. If you must keep it, you are maintaining a fork that swaps the transport for the HTTP v1 API (OAuth2 service account + https://fcm.googleapis.com/v1/projects/{id}/messages:send). For greenfield work prefer kreait/laravel-firebase or a v1-capable channel; treat this package as legacy glue.

Package age. Last release October 2020. Expect Composer constraint friction on Laravel 10/11 and PHP 8.2+. Check composer why-not before assuming it installed cleanly; a --ignore-platform-reqs install is a red flag, not a fix.

null token is a silent no-op. Returning null from routeNotificationForFcm() (e.g. user never granted permission) causes the channel to skip sending — no exception. Log token registration explicitly, otherwise "push doesn't work for some users" becomes an invisible-data bug.

data values must be strings. FCM rejects integers, booleans, and nested arrays in the data payload. Cast everything: ['order_id' => (string) $order->id].

4 KB payload ceiling. Notification + data combined. Truncate long message bodies before they hit the API; the error comes back as a generic 400.

Tokens expire and get rotated. A 404/NotRegistered from FCM means prune that token from your table. Without pruning you accumulate dead tokens and slow down every batch send. Wrap the send loop so one bad token doesn't abort the rest.

Config caching. After changing FCM_SERVER_KEY, run php artisan config:cache (or config:clear locally). Editing .env while a cached config is live is the classic "I rotated the key and nothing changed."

Never put PII in data. The payload is readable on-device and via any intermediary. Send an ID, fetch the record in-app.

Extension point. If you need v1 or per-tenant credentials, bind your own client into the container, or write a sibling channel (App\Notifications\Channels\FcmV1Channel) and keep the same toFcm() payload builder — FcmMessage is a plain value object and survives the transport swap.

Debug checklist for a failed send: is 'fcm' spelled right in via()? does routeNotificationForFcm() return a non-empty token? is the config cached? is the key the server key (legacy) rather than a v1 service-account credential? is the endpoint even still reachable? In that order — most reports stop at one of the first three.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package