如何在Laravel中实现Firebase实时通知?跨端推送新消息至全用户
Hey there! Let's walk through exactly how to build WhatsApp-style cross-platform notifications (Android, iOS, Web) using Firebase Realtime Database paired with Firebase Cloud Messaging (FCM). I've implemented this setup for a few chat apps, so I'll share the practical steps and code snippets you need.
First, make sure each of your platforms is properly integrated with both Firebase Realtime Database and FCM:
- Android: Add the
google-services.jsonfile, include FCM dependencies in yourbuild.gradle, and enable internet permissions. - iOS: Add
GoogleService-Info.plist, configure APNs certificates in Firebase Console, and enable push notifications in your Xcode project. - Web: Initialize Firebase in your web app, include the FCM SDK, and set up a service worker for background notifications.
You shouldn't send FCM notifications directly from client apps (it's insecure and can be abused). Instead, use Cloud Functions for Firebase to listen for new messages in Realtime Database and trigger FCM pushes automatically.
Step 2.1: Write the Cloud Function
Here's a sample TypeScript function that listens for new messages in a messages/{chatId}/{messageId} path, fetches the recipient's FCM token, and sends a notification:
import * as functions from "firebase-functions"; import * as admin from "firebase-admin"; admin.initializeApp(); export const sendMessageNotification = functions.database .ref("/messages/{chatId}/{messageId}") .onCreate(async (snapshot, context) => { const messageData = snapshot.val(); const { senderId, recipientId, text, chatId } = messageData; // Fetch recipient's FCM token from your users collection const recipientSnapshot = await admin.database().ref(`/users/${recipientId}/fcmToken`).once("value"); const recipientToken = recipientSnapshot.val(); if (!recipientToken) { functions.logger.log("No FCM token found for recipient"); return null; } // Fetch sender's info to show in notification const senderSnapshot = await admin.database().ref(`/users/${senderId}`).once("value"); const senderInfo = senderSnapshot.val(); // Build FCM notification payload const payload = { notification: { title: senderInfo.name, body: text, sound: "default", click_action: "OPEN_CHAT_ACTIVITY" // Android-specific: defines what happens on click }, data: { chatId: chatId, senderId: senderId, messageText: text }, token: recipientToken }; // Send the notification return admin.messaging().send(payload); });
Deploy this function using firebase deploy --only functions.
Now, let's configure each platform to receive and display the notification like WhatsApp.
3.1 Android Implementation
Step 3.1.1: Create a Notification Channel (Android 8.0+)
In your Application class or MainActivity, create a channel for chat notifications:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( "CHAT_NOTIFICATIONS", "Chat Messages", NotificationManager.IMPORTANCE_HIGH ).apply { description = "Notifications for incoming chat messages" enableVibration(true) vibrationPattern = longArrayOf(0, 500, 200, 500) } val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) }
Step 3.1.2: Handle FCM Messages with FirebaseMessagingService
Create a subclass to process incoming notifications:
class MyFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(remoteMessage: RemoteMessage) { super.onMessageReceived(remoteMessage) // Check if user is already in the chat activity for this chatId // (You'll need to track active chat state in your app) val isChatActive = MyAppState.isChatActive(remoteMessage.data["chatId"]) if (isChatActive) return // Don't show notification if user is already in the chat // Build WhatsApp-style notification val intent = Intent(this, ChatActivity::class.java).apply { putExtra("chatId", remoteMessage.data["chatId"]) flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } val pendingIntent = PendingIntent.getActivity( this, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) val notificationBuilder = NotificationCompat.Builder(this, "CHAT_NOTIFICATIONS") .setSmallIcon(R.drawable.ic_notification) .setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.sender_avatar)) // Replace with sender's avatar .setContentTitle(remoteMessage.notification?.title) .setContentText(remoteMessage.notification?.body) .setPriority(NotificationCompat.PRIORITY_HIGH) .setContentIntent(pendingIntent) .setAutoCancel(true) .setGroup("CHAT_GROUP_${remoteMessage.data["chatId"]}") // Group notifications by chat val notificationManager = NotificationManagerCompat.from(this) notificationManager.notify(remoteMessage.data["chatId"]?.hashCode() ?: 0, notificationBuilder.build()) } override fun onNewToken(token: String) { super.onNewToken(token) // Save this token to your Realtime Database under the user's profile FirebaseDatabase.getInstance().ref("/users/${currentUserId}/fcmToken").setValue(token) } }
Don't forget to register this service in your AndroidManifest.xml:
<service android:name=".MyFirebaseMessagingService" android:exported="false"> <intent-filter> <action android:name="com.google.firebase.MESSAGING_EVENT" /> </intent-filter> </service>
3.2 iOS Implementation
Step 3.2.1: Request Push Permissions
In your AppDelegate (or SceneDelegate for iOS 13+), request push permissions on app launch:
import FirebaseMessaging func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { FirebaseApp.configure() // Request push permissions UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in guard granted else { return } DispatchQueue.main.async { application.registerForRemoteNotifications() } } Messaging.messaging().delegate = self return true }
Step 3.2.2: Handle FCM Messages
Implement MessagingDelegate and handle incoming notifications:
extension AppDelegate: MessagingDelegate { func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { guard let token = fcmToken else { return } // Save token to Realtime Database Database.database().reference().child("users/\(currentUserId)/fcmToken").setValue(token) } func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { // Check if user is in the chat screen for this message let userInfo = notification.request.content.userInfo let chatId = userInfo["chatId"] as? String if ChatManager.shared.isActiveChat(chatId) { completionHandler([]) // Don't show notification } else { completionHandler([.banner, .sound, .badge]) } } func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let userInfo = response.notification.request.content.userInfo let chatId = userInfo["chatId"] as? String // Open chat screen if let window = UIApplication.shared.windows.first { let chatVC = ChatViewController() chatVC.chatId = chatId window.rootViewController = UINavigationController(rootViewController: chatVC) window.makeKeyAndVisible() } completionHandler() } }
3.3 Web Implementation
Step 3.3.1: Register Service Worker
Create a firebase-messaging-sw.js file in your web root:
importScripts("https://www.gstatic.com/firebasejs/9.23.0/firebase-app-compat.js"); importScripts("https://www.gstatic.com/firebasejs/9.23.0/firebase-messaging-compat.js"); firebase.initializeApp({ apiKey: "YOUR_API_KEY", authDomain: "YOUR_AUTH_DOMAIN", projectId: "YOUR_PROJECT_ID", storageBucket: "YOUR_STORAGE_BUCKET", messagingSenderId: "YOUR_SENDER_ID", appId: "YOUR_APP_ID" }); const messaging = firebase.messaging(); messaging.onBackgroundMessage((payload) => { const notificationTitle = payload.notification.title; const notificationOptions = { body: payload.notification.body, icon: "/icons/notification-icon.png", data: payload.data }; self.registration.showNotification(notificationTitle, notificationOptions); }); // Handle notification click self.addEventListener('notificationclick', (event) => { event.notification.close(); // Open chat page with chatId from data const chatId = event.notification.data.chatId; event.waitUntil( clients.openWindow(`/chat/${chatId}`) ); });
Step 3.3.2: Initialize FCM in Your Web App
In your main JS file:
import { initializeApp } from "firebase/app"; import { getMessaging, getToken, onMessage } from "firebase/messaging"; const firebaseConfig = { // Your Firebase config details }; const app = initializeApp(firebaseConfig); const messaging = getMessaging(app); // Request notification permission Notification.requestPermission().then((permission) => { if (permission === "granted") { getToken(messaging, { vapidKey: "YOUR_VAPID_KEY" }).then((currentToken) => { if (currentToken) { // Save token to Realtime Database via your backend endpoint fetch(`/api/save-fcm-token?token=${currentToken}`, { method: "POST" }); } }); } }); // Handle foreground messages onMessage(messaging, (payload) => { const chatId = payload.data.chatId; // Check if user is already in the chat tab if (window.location.pathname === `/chat/${chatId}`) return; // Show in-app notification (like WhatsApp's web toast) showInAppNotification(payload.notification.title, payload.notification.body); });
- Notification Grouping: Group notifications by chat (as shown in Android code with
setGroup) so users can expand/collapse chat threads. - Online Status Check: Track if a user is active in the chat to avoid sending unnecessary notifications (we added checks in each platform's code).
- Rich Notifications: Add sender avatars, long text previews, and action buttons (like "Reply" directly from the notification).
- Silent Notifications: For background sync, use FCM data messages instead of notification messages to update the app without showing a toast.
内容的提问来源于stack exchange,提问作者waleed ragab

