netosts/laravel-fcm-notifications

A robust and secure Firebase Cloud Messaging (FCM) notification system for Laravel applications

0.1.0 2025-07-02 21:12 UTC

This package is auto-updated.

Last update: 2025-07-03 00:05:50 UTC


README

Latest Version on Packagist Total Downloads License

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.

Table of Contents

Features

  • ๐Ÿš€ 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

Requirements

  • PHP 8.1 or higher
  • Laravel 10.0 or higher
  • Firebase project with FCM enabled

Installation

You can install the package via Composer:

composer require netosts/laravel-fcm-notifications

Publish the configuration file:

php artisan vendor:publish --tag=fcm-notifications-config

Quick Start

Get up and running in minutes:

1. Set up your Firebase credentials

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-----"

2. Add FCM token to your User model

// 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'];
}

3. Send your first notification

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.

Configuration

๐Ÿ’ก Tip: If you just want to get started quickly, check the Quick Start section above.

1. Firebase Setup

To use FCM, you need a Firebase project with proper credentials:

  1. Go to the Firebase Console
  2. Create a new project or select an existing one
  3. Navigate to Project Settings โ†’ Service Accounts
  4. Click Generate New Private Key to download the service account JSON file

2. Environment Variables

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 \n characters for line breaks.

3. Database Setup

Choose one of the following approaches for storing FCM tokens:

Option A: Multiple Tokens per User (Recommended)

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();
});

Option B: Single Token per User

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();
});

4. Token Management

Configure how the package discovers FCM tokens from your models:

For Multiple Tokens (Option A)

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);
    }
}

For Single Token (Option B)

class User extends Model
{
    protected $fillable = ['fcm_token'];

    // Optional: Custom method name
    public function getFcmToken()
    {
        return $this->fcm_token;
    }
}

Usage

Basic Usage

There are two main ways to send FCM notifications:

Method 1: Using FcmNotification Directly (Simple)

use LaravelFcmNotifications\Notifications\FcmNotification;

// Simple notification
$notification = new FcmNotification(
    title: 'New Message',
    body: 'You have a new message from John'
);

$user->notify($notification);

Method 2: Custom Notification Class (Recommended)

Create a custom notification class for better organization:

php artisan make:notification PushNotification

Extend 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);

Direct Service Usage

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'];
}

Message Types

FCM supports different message types for different use cases:

1. Notification + Data (Default)

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']
);

2. Data Only

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();

3. Notification Only

Shows only a system notification without custom data:

$notification = (new FcmNotification(
    title: 'System Maintenance',
    body: 'Our systems will be down for maintenance tonight'
))->notificationOnly();

Batch Sending

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";
    }
}

Platform-Specific Configuration

Customize notifications for different platforms:

Android Specific Settings

$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);

iOS Specific Settings

$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);

Cross-Platform Message

$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');

Event Listeners

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).

Testing

Built-in Test Commands

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-token

Token Validation

Validate FCM tokens before sending notifications:

use LaravelFcmNotifications\Facades\Fcm;

// Validate a single token
$isValid = Fcm::validateToken($deviceToken);

if ($isValid) {
    echo "Token is valid and ready to receive notifications";
} else {
    echo "Token is invalid or expired";
}

// Validate multiple tokens at once
$tokens = ['token1', 'token2', 'token3'];
$result = Fcm::validateTokens($tokens);

echo "Valid tokens: " . count($result['valid']) . "\n";
echo "Invalid tokens: " . count($result['invalid']) . "\n";

// Remove invalid tokens
foreach ($result['invalid'] as $invalidToken) {
    // Remove from your database
    NotificationToken::where('token', $invalidToken)->delete();
}

Testing in Development

// 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');
}

Configuration Options

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
];

Configuration Details

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

Troubleshooting

Common Issues

๐Ÿ” Authentication Failed

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_id matches your Firebase project
# Test your credentials
php artisan fcm:test --token=test-token

๐Ÿ” Tokens Not Found

Symptoms: No notifications sent, "no tokens found" errors

Solutions:

  • Check your token storage implementation
  • Verify the token_column configuration 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 token

๐Ÿ“ฑ Messages Not Received

Symptoms: 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
>>> Fcm::validateToken('your-device-token')

โšก Performance Issues

Symptoms: 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;
}

Debug Mode

Enable detailed logging for troubleshooting:

# In your .env file
LOG_LEVEL=debug
APP_DEBUG=true

This will log detailed FCM request/response information to help diagnose issues.

Getting Help

If you're still having issues:

  1. Check the logs - Look in storage/logs/laravel.log for detailed error messages
  2. Test with the built-in commands - Use php artisan fcm:test to isolate issues
  3. Validate your setup - Double-check Firebase credentials and configuration
  4. Review the FCM documentation - Some issues may be Firebase-specific

Changelog

Please see CHANGELOG for more information on what has changed recently.

Security

If you discover any security-related issues, please email netostt91@gmail.com instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

Support

Need help? Here's how to get support:

โญ If this package helped you, please consider giving it a star on GitHub! โญ