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

Fcm Bundle Laravel Package

redjanym/fcm-bundle

A Symfony bundle for Firebase Cloud Messaging (FCM) that provides simple services and configuration to send push notifications to Android and iOS devices through Google's FCM HTTP API.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the framework reality check. redjanym/fcm-bundle is a Symfony bundle, not a Laravel package. Laravel has no bundles.php, no config tree, and no autodiscovery for Symfony bundles — so you cannot composer require it and inject its services by class name the way a Symfony developer would. Two realistic paths:

  1. Laravel (recommended): require the framework-agnostic library it wraps — redjanym/php-fcm-v1 — and bind it yourself. You get the same FcmClient/Message API without the bundle scaffolding.
  2. Symfony alongside your Laravel work: register the bundle in config/bundles.php, drop in config/packages/redjanym_fcm.yaml, and inject FcmClient directly.

PHP 8+ as of v2.1.1. The package now declares PHP 8+ support, which finally lines it up with Laravel 9/10/11/12's own floor (8.0/8.1/8.2). Practically this means: if you were blocked before because Composer refused to install it on a PHP 8 runtime, that blocker is gone. If you are still pinned to PHP 7.4, you are stuck on an older release — in which case go straight to option 1 and skip the bundle entirely.

Steps for a Laravel app:

composer require redjanym/php-fcm-v1
  • Firebase Console → Project settings → Service accounts → Generate new private key. Save the JSON outside the web root, e.g. storage/app/firebase/service-account.json.
  • Add FCM_CREDENTIALS=storage/app/firebase/service-account.json to .env, and do not commit the JSON.
  • Bind it once in a service provider:
$this->app->singleton(FcmClient::class, fn () => new FcmClient(
    storage_path('app/firebase/service-account.json')
));

Where to look first: the README's usage block and the FcmClient / Message classes in vendor/redjanym/. They are small; read them before trusting them.

First use case: send a single-token notification in a queued job, not in a controller.

$message = new Message($deviceToken, [
    'title' => 'Your order shipped',
    'body'  => 'Track it in the app',
], ['order_id' => (string) $order->id]);

$client->send($message);

Implementation Patterns

Wrap it in a domain service, not raw calls. Keep FcmClient out of controllers and jobs' internals; expose something like PushNotifier::toUser(User $user, PushPayload $payload). That gives you one place to add logging, token pruning, and retries.

Use a Laravel Notification channel. A small FcmChannel that reads routeNotificationForFcm() returns $this from via(), and maps the notification's toFcm() array onto Message. This keeps push notifications alongside mail/database notifications and lets queueing come for free.

Handle multiple devices per user. Store device_tokens in their own table with user_id, token, platform, last_seen_at. Loop the tokens, send individually, and collect per-token failures rather than failing the whole batch.

Queue every send. HTTP v1 is a network call with OAuth token exchange on cold start. Use a job with $tries = 3 and backoff(); classify errors so only 5xx and timeouts retry — 4xx token errors should not.

Prune aggressively. When a response indicates UNREGISTERED or INVALID_ARGUMENT, delete the token row immediately. SENDER_ID_MISMATCH means a token from another Firebase project — flag it, don't retry.

Test without Firebase. Put your wrapper behind an interface (PushSender) and bind a NullPushSender / RecordingPushSender in tests. Do not let feature tests hit Google's endpoints.

Multi-tenant or multi-project: the client is bound to one service account. If you serve several Firebase projects, register named singletons (fcm.tenant_a) rather than mutating a shared client.

Gotchas and Tips

  • Symfony-only surface area. The bundle's value is its config tree and DI wiring; in Laravel you get neither. Don't try to make the bundle's services resolve — use the underlying library.
  • PHP 8+ is now hard-required. v2.1.1 sets the floor at PHP 8. If any service in your fleet (legacy worker container, CI image, an old Octane box) still runs 7.4, composer update will fail on that job rather than degrade gracefully. Align your runtime matrix before bumping, and pin redjanym/php-fcm-v1 to a compatible tag if you must straddle both.
  • Config key names drift. Check the bundle's README for the exact credential key (account_key_path, service_account_file, etc.) — it is the one required option, and a wrong key silently yields an unconfigured client.
  • %kernel.project_dir% does not exist in Laravel. Use storage_path() / base_path(). A relative path resolves against the CLI's CWD, which breaks under queue workers and Octane.
  • OAuth token lifetime. The client authenticates with a short-lived bearer token (~1 hour). Fine for HTTP requests; risky in long-running workers or Octane if the client caches credentials in-process. Re-resolve the client per job or ensure refresh logic exists.
  • Payload constraints. data values must be strings (ints/bools get rejected or stringified unpredictably). Total message size ≤ 4 KB; topic conditions have their own limits.
  • Advanced options may be missing. Platform-specific blocks (android.notification, apns.payload.aps, webpush), collapse keys, TTL, and priority may not be exposed by Message. If you need them, either extend the message class or drop to google/auth + Guzzle and post the raw JSON yourself.
  • Exception behavior. Assume 4xx responses throw. Wrap sends in try/catch and log the FCM errorCode — it is the only reliable way to distinguish "device gone" from "malformed payload".
  • Credential hygiene. Mount the JSON as a secret in production (/run/secrets/fcm.json), not a file copied into the image. Rotate the key in Firebase if it ever lands in a log.
  • Low-traffic dependency. 43 stars and no dependents means you are the QA team. A one-line "PHP 8+ support" patch is the kind of release cadence to expect. Pin the version in composer.json, and be ready to replace it with ~50 lines of google/auth + Guzzle if maintenance stalls. The HTTP v1 API itself is simple; the library is not load-bearing.
  • Debugging checklist: can PHP read the JSON? does project_id inside it match the tokens you're sending? is the device token from the same Firebase project? These three account for most failures.
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