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 Laravel Package

williamcruzme/laravel-fcm

Laravel FCM is a Laravel package for sending Firebase Cloud Messaging push notifications. It offers a simple API for targeting devices, topics, and conditions with notification and data payloads, making mobile push setup straightforward.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the package

    composer require williamcruzme/laravel-fcm
    

    On Laravel 5.5+ the service provider is auto-discovered. On older versions, register Williamcruzme\Fcm\FcmServiceProvider in config/app.php.

  2. Publish the config

    php artisan vendor:publish --provider="Williamcruzme\Fcm\FcmServiceProvider"
    

    This drops config/fcm.php into your app. Add the credentials it expects to .env:

    FCM_SERVER_KEY=your_legacy_server_key_from_firebase_console
    

    You get this key in the Firebase Console → Project Settings → Cloud Messaging → Server key (legacy tab).

  3. Store device tokens on the notifiable model

    Add the HasFcmTokens trait (or whichever token trait the package ships) to your User model, and run the migration the package provides for the fcm_tokens table:

    use Williamcruzme\Fcm\HasFcmTokens;
    
    class User extends Authenticatable
    {
        use HasFcmTokens;
    }
    

    Tokens are the long opaque strings your mobile/web client gets from firebase.messaging().getToken() and POSTs to your API.

  4. Send your first notification

    use Illuminate\Notifications\Notification;
    use Williamcruzme\Fcm\FcmChannel;
    use Williamcruzme\Fcm\FcmMessage;
    
    class OrderShipped extends Notification
    {
        public function via($notifiable)
        {
            return [FcmChannel::class];
        }
    
        public function toFcm($notifiable)
        {
            return (new FcmMessage())
                ->notification([
                    'title' => 'Order shipped',
                    'body'  => 'Your order is on its way.',
                ])
                ->data(['order_id' => (string) $notifiable->id]);
        }
    }
    
    $user->notify(new OrderShipped());
    

First thing to look at: the published config/fcm.php and the FcmMessage class — those two define 90% of what you can actually do. Then check vendor/williamcruzme/laravel-fcm/src/ for the channel's send() method to confirm exactly how payload keys are marshalled before you build complex payloads against it.

Implementation Patterns

One notification, many devices routeNotificationForFcm() on the notifiable is the extension point. The default trait returns every registered token, but override it to scope:

public function routeNotificationForFcm()
{
    return $this->fcmTokens()->pluck('token')->all();
}

Returning an array fans out to all tokens in a single request (FCM's legacy registration_ids supports up to 1000). For larger user bases you need to chunk — the package won't do it for you.

Data-only vs notification payloads Two distinct delivery semantics, and the choice matters more than the API:

  • ->notification([...]) — FCM renders the OS-level banner. App killed or backgrounded: banner shown automatically. Good for user-facing alerts.
  • ->data([...]) — silent payload. Delivered to the app's messaging callback; app decides what to do. Good for cache invalidation, badge counters, background sync.
  • Both together — iOS treats this as notification first; the data block is only surfaced when the user taps. Android delivers both, but the data is wrapped under data in the intent extras.

Targeting a topic instead of tokens For broadcast-style messages ("all users on v2.3"), have clients subscribe to a topic (/topics/version_2_3) and send with the topic target rather than iterating tokens. This is dramatically cheaper and avoids token-staleness entirely. The package's FcmMessage typically exposes a to() / toTopic() style method — check the source, since the exact name varies across versions.

Queuing Notifications already implement ShouldQueue when you add the interface to your Notification class, and the FCM channel will then run inside a queue worker. Do this in production — FCM calls are outbound HTTP and will otherwise add hundreds of milliseconds to your request.

Token lifecycle Tokens rotate (app reinstall, restore-from-backup, FCM-side rotation). Treat the fcm_tokens table as append-and-prune:

  • Client POSTs a token on login → upsert.
  • Client onTokenRefresh handler POSTs the new token → upsert + delete old.
  • Server prunes on send failure (NotRegistered, InvalidRegistration in the response) — the package usually doesn't auto-delete, so wire this into your own response handling.

Composing reusable payloads FcmMessage is fluent, so extract a base builder:

protected function baseMessage(): FcmMessage
{
    return (new FcmMessage())
        ->data(['app_version' => config('app.version')])
        ->priority('high');
}

Gotchas and Tips

The legacy FCM API is dead. This is the single biggest risk with this package. It targets https://fcm.googleapis.com/fcm/send, which Google shut down in mid-2024 in favour of the HTTP v1 API with OAuth2 service accounts. If you're starting a new project today, evaluate kreait/laravel-firebase or laravel-notification-channels/fcm before committing — otherwise you'll ship a package that fires into a 404. For legacy projects this package still works as a template; the fluent FcmMessage / FcmChannel pattern is worth copying even if you swap the underlying transport.

Server-key auth vs service-account auth. FCM_SERVER_KEY is the legacy credential; the HTTP v1 API needs a service-account JSON and a short-lived OAuth token. If you keep this package, you'll need to patch the HTTP client layer, not just the config. Watch for the token-endpoint call being blocking — cache the access token for ~55 minutes.

notification vs data shape mismatches. A very common bug: you put order_id as an integer in data, and Android receives it as a string (FCM coerces all data values to strings). Cast on the client side and always send strings for consistency.

iOS silent pushes require extra keys. Data-only messages won't arrive on iOS unless the payload includes content_available: true and, on some iOS versions, priority: 'high'. The package won't add these for you — include them in the raw payload via whatever raw-payload setter FcmMessage exposes.

Response handling is your job. The channel's send() typically returns the raw FCM JSON, and it does not throw on per-token failures. You'll see a 200 OK with results: [{error: "NotRegistered"}] — treat HTTP success as "request delivered", and inspect the results array for per-token outcomes. Wire a listener or override the channel if you need automatic token pruning.

1000-token limit. FCM's legacy fan-out caps registration_ids at 1000 per request. Anything larger needs client-side chunking, and you're rate-limited (roughly 600 requests/minute on the legacy endpoint) — queue with backoff.

routeNotificationForFcm returning a single string vs array. Different versions of the package disagree on whether to accept a string or array. If sends are silently missing devices, dump the value — a single token string being treated as an array of characters is a fun one to debug.

Testing. Wrap sends in Notification::fake() for the notification side, and bind a fake FcmChannel (or intercept Guzzle) for the transport side. Assert on Notification::assertSentTo($user, OrderShipped::class) rather than trying to mock the HTTP call — the latter bakes your tests to FCM's JSON schema, which will change when you migrate to v1.

Extension point to remember: if you need to add headers, retries, or swap the transport, the cleanest lever is replacing the channel binding in your AppServiceProvider — the Notification classes stay untouched, which keeps migrations to FCM v1 cheap later.

Topic subscriptions can't be created server-side from this package. Client (Android/iOS/JS) must call subscribeToTopic. Server-side topic management requires the Admin SDK (kreait/laravel-firebase), not a legacy-key package. Plan for this if "send to all users" is on your roadmap.

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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib