laravel-notification-channels/gcm
Laravel notification channel for sending push notifications through Google Cloud Messaging (GCM). It integrates GCM with Laravel’s notification system, letting you deliver mobile push messages through a dedicated channel.
1. Install and register
composer require laravel-notification-channels/gcm
On Laravel 5.5+ the NotificationChannels\Gcm\GcmServiceProvider is auto-discovered. On older versions add it to config/app.php under providers.
2. Publish and configure
php artisan vendor:publish --provider="NotificationChannels\Gcm\GcmServiceProvider"
This writes config/gcm.php. Put your GCM/FCM legacy server key in GCM_KEY in .env (the package reads it from config, so never hardcode it). The key is sent as the Authorization: key=... header against Google's legacy gcm/send endpoint.
3. Teach your notifiable how to resolve a device token
// app/Models/User.php
public function routeNotificationForGcm()
{
return $this->gcm_token; // single token
// return $this->devices->pluck('token')->all(); // multiple tokens
}
4. Send your first notification
use NotificationChannels\Gcm\GcmChannel;
use NotificationChannels\Gcm\GcmMessage;
class OrderShipped extends Notification
{
public function via($notifiable)
{
return [GcmChannel::class];
}
public function toGcm($notifiable)
{
return GcmMessage::create()
->title('Order shipped')
->body("Order #{$this->order->id} is on its way")
->data(['order_id' => (string) $this->order->id]);
}
}
Then $user->notify(new OrderShipped($order));. If nothing arrives, check in this order: (a) is GCM_KEY set and is config cached with the old value, (b) does routeNotificationForGcm() return a non-empty string, (c) does the device actually hold that registration token.
Where to look first: the package's src/GcmMessage.php is the whole API surface — read it before anything else. It is a small, single-purpose package (52 stars, last release 2017-08-10, archived), so the source is the documentation.
Notification vs. data messages. The most important design decision with this package:
title()/body(), GCM delivers a notification message — the OS draws the tray entry itself when the app is backgrounded, and your onMessageReceived handler is not called.data([...]), it is a data message — your Android code always receives it, but you must build the notification manually.Mixing both (title/body + data) is the common and recommended pattern: the tray entry is free, and the data payload is available when the user taps through.
Fan-out to multiple devices. Returning an array from routeNotificationForGcm() sends all tokens in one request (legacy GCM accepts up to 1000 registration IDs per call). For an account with many devices, prefer storing tokens in their own table with user_id, token, platform, last_seen_at, and prune aggressively:
public function routeNotificationForGcm()
{
return $this->pushTokens()->pluck('token')->all();
}
Model the payload once. Keep a base notification or a small builder so title/icon/sound/priority stay consistent across the app:
abstract class PushNotification extends Notification
{
public function via($notifiable) { return [GcmChannel::class]; }
protected function base(): GcmMessage
{
return GcmMessage::create()
->icon('ic_notification')
->sound('default')
->priority('high');
}
}
Queue everything. GCM sends are HTTP calls in the request path. Implement ShouldQueue on the notification (or use Notification::send from a job). Add ->timeToLive() when the message is only useful briefly (e.g. "driver is 2 minutes away") and ->priority('high') when it must punch through Doze.
Combine channels. via() is the natural feature-flag point — return [GcmChannel::class, MailChannel::class] only for users who have a token, and [MailChannel::class] otherwise. Returning null from toGcm() is the documented way to skip a push for a single recipient without touching via().
Prune dead tokens on response. The legacy API returns per-token results, and stale tokens come back as NotRegistered / InvalidRegistration. The package surfaces send failures via NotificationChannels\Gcm\Exceptions\CouldNotSendNotification, but per-token errors inside a batch response are your responsibility — hook the send path (a listener, or a thin wrapper service around $notifiable->notify()) and delete tokens that come back dead. Also handle canonical_ids: Google tells you the token has rotated, and you should replace the stored value or you will keep sending to the old one.
Test with fakes, not HTTP.
Notification::fake();
$user->notify(new OrderShipped($order));
Notification::assertSentTo($user, OrderShipped::class, function ($n, $channels) {
return in_array(GcmChannel::class, $channels);
});
Unit-test payload construction directly by instantiating the notification and asserting on the GcmMessage getters — that is far cheaper than faking the client.
Integration tip: because the package is archived, wrap it. Put GcmChannel behind your own PushSender interface with one send(User $user, PushPayload $payload) method, so a future move to FCM (or Laravel's own fcm channel ecosystem) is a one-class change rather than a grep across every notification.
data block. ['order_id' => (string) $id], not ['order_id' => $id]. Serialize anything structured with json_encode before it goes in.GCM_KEY, run php artisan config:clear (or re-run config:cache). A 401 InvalidRegistration/auth failure after a key rotation is almost always a stale cached config.via() must name the channel class (GcmChannel::class). The string 'gcm' only works if the channel is registered under that alias — don't assume.null from toGcm() skips the push. Useful, but silent: if pushes "just stop" for one user, log when you return null.routeNotificationForGcm() with a ->filter()->values() and skip early.title()/body() — the tray shows it, but onMessageReceived was never called. This is the single most common bug report with this package.tinker: (new OrderShipped($order))->toGcm($user) and inspect the resulting GcmMessage — confirms the payload shape without touching Google.How can I help you explore Laravel packages today?