A robust and secure Firebase Cloud Messaging (FCM) notification system for Laravel applications. This package provides a comprehensive solution for sending push notifications with automatic token management, cleanup, and support for all FCM message types.
- Features
- Requirements
- Installation
- Quick Start
- Configuration
- Usage
- Testing
- Configuration Options
- Troubleshooting
- Support
- π Easy Integration - Drop-in Laravel notification channel
- π Secure Authentication - JWT-based Google OAuth2 authentication
- π± Multiple Message Types - Support for notification-only, data-only, and combined messages
- π Automatic Token Cleanup - Removes invalid tokens automatically
- π Batch Sending - Send to multiple devices efficiently
- π οΈ Platform Specific - Android and iOS specific configurations
- π Comprehensive Logging - Detailed logging for debugging
- β‘ Performance Optimized - Token caching and efficient API calls
- π§ͺ Testing Commands - Built-in commands for testing functionality
- PHP 8.1 or higher
- Laravel 10.0 or higher
- Firebase project with FCM enabled
You can install the package via Composer:
composer require netosts/laravel-fcm-notificationsPublish the configuration file:
php artisan vendor:publish --tag=fcm-notifications-configGet up and running in minutes:
Add these environment variables to your .env file:
FCM_PROJECT_ID=your-firebase-project-id
FCM_CLIENT_EMAIL=your-service-account@your-project.iam.gserviceaccount.com
FCM_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nYour-Private-Key-Here\n-----END PRIVATE KEY-----"// Add to your users table migration
Schema::table('users', function (Blueprint $table) {
$table->string('fcm_token')->nullable();
});
// Make it fillable in your User model
class User extends Model
{
protected $fillable = ['fcm_token'];
}use LaravelFcmNotifications\Notifications\FcmNotification;
$notification = new FcmNotification(
title: 'Welcome!',
body: 'Thanks for joining our app'
);
$user->notify($notification);That's it! Your notification will be sent to the user's device.
π‘ Tip: If you just want to get started quickly, check the Quick Start section above.
To use FCM, you need a Firebase project with proper credentials:
- Go to the Firebase Console
- Create a new project or select an existing one
- Navigate to Project Settings β Service Accounts
- Click Generate New Private Key to download the service account JSON file
Add the following variables to your .env file:
# Required - Firebase Credentials
FCM_PROJECT_ID=your-firebase-project-id
FCM_CLIENT_EMAIL=your-service-account@your-project.iam.gserviceaccount.com
FCM_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nYour-Private-Key-Here\n-----END PRIVATE KEY-----"
# Optional - API Settings
FCM_TIMEOUT=30
FCM_DEFAULT_MODE=data_only
# Optional - Token Management
FCM_TOKEN_COLUMN=token
FCM_AUTO_CLEANUP_TOKENS=true
β οΈ Important: The private key must include\ncharacters for line breaks.
Choose one of the following approaches for storing FCM tokens:
For users with multiple devices, create a dedicated tokens table:
// Create migration: php artisan make:migration create_notification_tokens_table
Schema::create('notification_tokens', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->string('token')->unique();
$table->string('device_type')->nullable(); // 'android', 'ios', 'web'
$table->timestamps();
});Add a token column to your existing users table:
// Add to your users table migration
Schema::table('users', function (Blueprint $table) {
$table->string('fcm_token')->nullable();
});Configure how the package discovers FCM tokens from your models:
class User extends Model
{
public function notificationTokens()
{
return $this->hasMany(NotificationToken::class);
}
}
// Optional: Create a NotificationToken model
class NotificationToken extends Model
{
protected $fillable = ['user_id', 'token', 'device_type'];
public function user()
{
return $this->belongsTo(User::class);
}
}class User extends Model
{
protected $fillable = ['fcm_token'];
// Optional: Custom method name
public function getFcmToken()
{
return $this->fcm_token;
}
}There are two main ways to send FCM notifications:
use LaravelFcmNotifications\Notifications\FcmNotification;
// Simple notification
$notification = new FcmNotification(
title: 'New Message',
body: 'You have a new message from John'
);
$user->notify($notification);Create a custom notification class for better organization:
php artisan make:notification PushNotificationExtend the FcmNotification class:
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use LaravelFcmNotifications\Notifications\FcmNotification;
class PushNotification extends FcmNotification implements ShouldQueue
{
use Queueable;
// Add custom logic here if needed
}Use your custom notification:
use App\Notifications\PushNotification;
$notification = new PushNotification(
title: 'New Message',
body: 'You have a new message from John',
image: 'https://example.com/avatar.jpg',
data: ['message_id' => '123', 'sender_id' => '456']
);
$user->notify($notification);For more control, use the FCM service directly:
use LaravelFcmNotifications\Facades\Fcm;
use LaravelFcmNotifications\Services\FcmMessage;
// Create a detailed message
$message = FcmMessage::create(
title: 'Direct Message',
body: 'This message was sent directly via the service'
)
->addData('custom_key', 'custom_value')
->addData('user_action', 'view_profile')
->setAndroidPriority('high')
->setIosBadge(1);
// Send to a specific device
$result = Fcm::sendToDevice($deviceToken, $message);
// Handle the result
if ($result['success']) {
echo "Message sent successfully!";
} else {
echo "Failed to send: " . $result['error'];
}FCM supports different message types for different use cases:
Shows a system notification and passes custom data to your app:
$notification = new FcmNotification(
title: 'New Order',
body: 'You received a new order #1234',
data: ['order_id' => '1234', 'action' => 'view_order']
);Sends data silently to your app without showing a notification:
$notification = (new FcmNotification(
title: 'Background Sync', // Not shown to user
body: 'Data updated', // Not shown to user
data: ['sync' => 'true', 'timestamp' => time()]
))->dataOnly();Shows only a system notification without custom data:
$notification = (new FcmNotification(
title: 'System Maintenance',
body: 'Our systems will be down for maintenance tonight'
))->notificationOnly();Send the same message to multiple devices efficiently:
$deviceTokens = ['token1', 'token2', 'token3'];
$message = FcmMessage::create(
title: 'System Announcement',
body: 'Important update for all users'
);
$result = Fcm::sendToMultipleDevices($deviceTokens, $message);
// Check results
echo "Successfully sent to: {$result['summary']['success']} devices\n";
echo "Failed to send to: {$result['summary']['failure']} devices\n";
// Handle individual failures
foreach ($result['details'] as $detail) {
if (!$detail['success']) {
echo "Failed for token: {$detail['token']}, Error: {$detail['error']}\n";
}
}Customize notifications for different platforms:
$message = FcmMessage::create('Android Notification', 'Optimized for Android devices')
->setAndroidChannel('important_notifications')
->setAndroidPriority('high')
->setAndroidSound('custom_sound.mp3')
->addData('android_specific', 'value');
$user->notify($notification);$message = FcmMessage::create('iOS Notification', 'Optimized for iOS devices')
->setIosBadge(5) // Badge count
->setIosSound('custom_sound.caf') // Custom sound
->addData('ios_specific', 'value');
$user->notify($notification);$message = FcmMessage::create('Universal Message', 'Works on all platforms')
// Android settings
->setAndroidPriority('high')
->setAndroidChannel('notifications')
// iOS settings
->setIosBadge(1)
->setIosSound('default')
// Common data
->addData('universal_data', 'value');The package automatically handles token cleanup through events:
// In your EventServiceProvider
protected $listen = [
\LaravelFcmNotifications\Events\UnregisteredFcmTokenDetected::class => [
\LaravelFcmNotifications\Listeners\CleanupUnregisteredFcmToken::class,
],
];When an invalid token is detected, it's automatically removed from your database (if FCM_AUTO_CLEANUP_TOKENS=true).
The package includes helpful commands for testing your FCM integration:
# Test basic FCM functionality with a real device token
php artisan fcm:test --token=your-actual-device-token
# Test the direct service (bypasses Laravel notifications)
php artisan fcm:test --token=your-device-token --direct
# Test token cleanup functionality
php artisan fcm:test-cleanup your-device-tokenValidate FCM tokens before sending notifications:
use LaravelFcmNotifications\Facades\Fcm;
// Validate a single token
$validation = Fcm::validateToken($deviceToken);
if ($validation['valid']) {
echo "Token is valid and ready to receive notifications";
} else {
echo "Token is invalid: " . $validation['message'];
// Error type: $validation['error_type']
}
// Validate multiple tokens at once
$tokens = ['token1', 'token2', 'token3'];
$results = Fcm::validateTokens($tokens);
$validCount = 0;
$invalidCount = 0;
foreach ($results as $result) {
if ($result['valid']) {
$validCount++;
} else {
$invalidCount++;
// Remove invalid token from database
NotificationToken::where('token', $result['token'])->delete();
}
}
echo "Valid tokens: {$validCount}\n";
echo "Invalid tokens: {$invalidCount}\n";// Only send notifications in production
if (app()->environment('production')) {
$user->notify(new PushNotification('Production Alert', 'This is live!'));
} else {
// Log instead of sending during development
Log::info('Would send notification: Production Alert');
}The config/fcm-notifications.php file provides comprehensive configuration options:
return [
// Firebase Credentials (Required)
'project_id' => env('FCM_PROJECT_ID'),
'client_email' => env('FCM_CLIENT_EMAIL'),
'private_key' => env('FCM_PRIVATE_KEY'),
// API Settings
'timeout' => env('FCM_TIMEOUT', 30), // Request timeout in seconds
// Message Behavior
'default_mode' => env('FCM_DEFAULT_MODE', 'data_only'), // 'notification_only', 'data_only', 'both'
// Token Management
'token_column' => env('FCM_TOKEN_COLUMN', 'token'), // Column name for single token storage
'auto_cleanup_tokens' => env('FCM_AUTO_CLEANUP_TOKENS', true), // Auto-remove invalid tokens
// Performance Settings
'cache_token' => true, // Cache JWT tokens
'cache_prefix' => 'fcm_notifications_token', // Cache key prefix
];| Option | Description | Default |
|---|---|---|
project_id |
Your Firebase project ID | Required |
client_email |
Service account email | Required |
private_key |
Service account private key | Required |
timeout |
HTTP request timeout (seconds) | 30 |
default_mode |
Default message type | data_only |
token_column |
Single token column name | token |
auto_cleanup_tokens |
Auto-remove invalid tokens | true |
cache_token |
Cache JWT authentication tokens | true |
Symptoms: Authentication errors, 401 responses from FCM
Solutions:
- Verify your Firebase service account credentials in
.env - Ensure the private key includes proper line breaks (
\n) - Check that your service account has FCM permissions
- Validate your
project_idmatches your Firebase project
# Test your credentials
php artisan fcm:test --token=test-tokenSymptoms: No notifications sent, "no tokens found" errors
Solutions:
- Check your token storage implementation
- Verify the
token_columnconfiguration matches your database - Ensure users have FCM tokens stored
- Check relationship methods in your User model
// Debug token discovery
$user = User::find(1);
dd($user->notificationTokens); // For multiple tokens
dd($user->fcm_token); // For single tokenSymptoms: API calls succeed but notifications don't appear
Solutions:
- Test with the built-in test command
- Check FCM token validity
- Verify your mobile app is properly configured for FCM
- Check device notification settings
- Ensure app is not in battery optimization mode (Android)
# Validate your tokens
php artisan tinker
>>> $result = Fcm::validateToken('your-device-token');
>>> dd($result); // Shows detailed validation resultsSymptoms: Slow notification sending, timeouts
Solutions:
- Enable token caching in config
- Use batch sending for multiple recipients
- Implement queue processing for large volumes
- Increase timeout setting if needed
// Use queued notifications for better performance
class PushNotification extends FcmNotification implements ShouldQueue
{
use Queueable;
}Enable detailed logging for troubleshooting:
# In your .env file
LOG_LEVEL=debug
APP_DEBUG=trueThis will log detailed FCM request/response information to help diagnose issues.
If you're still having issues:
- Check the logs - Look in
storage/logs/laravel.logfor detailed error messages - Test with the built-in commands - Use
php artisan fcm:testto isolate issues - Validate your setup - Double-check Firebase credentials and configuration
- Review the FCM documentation - Some issues may be Firebase-specific
Please see CHANGELOG for more information on what has changed recently.
If you discover any security-related issues, please email netostt91@gmail.com instead of using the issue tracker.
- Neto Santos - Creator and maintainer
- All Contributors - Community contributors
The MIT License (MIT). Please see License File for more information.
Need help? Here's how to get support:
- π§ Email: netostt91@gmail.com
- π Bug Reports: GitHub Issues
- π¬ Questions & Discussions: GitHub Discussions
- π Documentation: This README and inline code documentation
β If this package helped you, please consider giving it a star on GitHub! β
