Notifications & notifications push
Sur mobile, il existe deux type de notifications, les notifications classiques comme lorsque vous téléchargez un fichier ou suite à une action spécifique. Et les notifications push qui sont envoyées à l'appareil si celui-ci n'utilise pas l'application concernée par la notification, pour tester il est faut donc se trouver sur une autre application ou en mode accueil. Ces notifications push doivent donc être envoyées par une API REST suite à des évènements particuliers, par exemple un nouveau tweet pour Twitter, un like sur une publication Facebook...
La manière la plus simple de gérer ces notifications est d'envoyer l'évènement à Firebase Cloud Messaging, service de Firebase, pour que celui-ci envoit la notification push à tous les appareils concernés. Il est possible d'envoyer une notification à l'ensemble des appareils possédant l'application ou de cibler un groupe ou un appareil spécifique à travers ce qui est appelé les topics
(groupe d'appareil) ou les tokens.
Note : Firebase Cloud Messaging est souvent référencé en tant que FCM.
WARNING
Si l'application n'est pas en APP_ENV=production
, la notification push sera envoyée en topics/test
qui ne sera pas reçu par l'application si elle a été installée en mode release (comme depuis le Google Play ou via flutter install
). Une notification push n'est donc visible qu'en mode debug, avec flutter run
, si elle est envoyée depuis l'application back-end en local.
TODO:
- about legacy and v1 FCM API (https://firebase.google.com/docs/cloud-messaging/migrate-v1 )
- Laravel packages for v1 auth and conflicts
- about flutter cloud messaging flutter package vs flutterfire documentation
Get token
POST https://domain.com/api/login
Body
form-data
email: user@mail.com
password: password
device_name: device_name
Authentification
TODO:
- get google-services.json and why
- about clean project for server key refresh
Topics & channels Android
TODO:
- about topics (set it on creation, subscription on app)
- about android channels
FCM can send push notifications to application by specific topic. A topic is a channel (not a notification android channel, it's FCM channel) created by FCM on Flutter application. You can create multiple topics where current user will be subscribed. If user is subscribed to a topic, they receive push notification, otherwise they won't receive it. You can check lib/services/NotificationProvider.dart
and you will see how it's works.
// here user will receive all push notifications send to '/topics/new_content'
messaging.subscribeToTopic('new_content');
With a little trick, you can define a user's topic.
// here push notification can be send just to 'investor_id_${investor.id}'
messaging.subscribeToTopic(userTopic);
And just for test, when you have a debug app, you can use test topic. Just debug applications will receive this notification.
messaging.subscribeToTopic('test');
Android channels
It's jsut for Android, you can create multiple channels for type of push notification and user can mute a channel to stop notification but they can receive others notifications. You can check current Android channels on lib/services/NotificationProvider.dart
.
Astuce
Si vous ne spécifiez pas de channel Android pour votre notification ou que le channel n'existe pas, c'est le channel par défaut qui sera utilisé.
const AndroidNotificationChannel newSubscriptionsOpened = AndroidNotificationChannel( 'new_subscriptions_opened', 'Title', 'Text', );await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.createNotificationChannel(newSubscriptionsOpened);
Tester les notifications
Attention
Si vous testez l'envoi de notifications sur un topic
qui est installé par défaut sur les releases publiées, tous les utilisateurs verront ces tests. Il est donc plus prudent de créer un topic spécifique sur votre appareil pour tester un envoi par topic.
Un test envoyé par la console de Cloud Messaging à l'application aura exactement le même effet, tous les appareils disposant du google-services.json
associé au projet receveront la notification.
Conseil : utiliser le topic
de test
et faire les tests par cURL ou par Postman, ce topic n'est ajouté que sur une application en mode debug
. Il est aussi possible d'envoyer une notification à un utilisateur spécifique avec le topic dédié nommé d'après son id : investor_id_#
.
Cloud Messaging
Il est possible de tester directement l'envoi de notifications dans la console de Cloud Messaging.
TODO
cURL
cURL est la méthode la plus simple pour tester les notifications push. La commande suivante est à entrer en bash et ne fonctionnera pas en PowerShell.
curl --location --request POST 'https://fcm.googleapis.com/fcm/send' \ --header 'Content-Type: application/json' \ --header 'Authorization:key=fcm_key' \ --data-raw '{ "notification": { "body": "Notification from curl", "title": "You have a new message (curl)." "android_channel_id": "misc", }, "priority": "high", "to": "/topics/test" }'
Pour tester les notifications push sur les applications en production, vous pouvez modifier "to": "/topics/test"
pour "to": "/topics/new_content"
. Attention, cela enverra la notification à TOUS les appareils utilisant cette application.
Postman
TODO: postman with FCM API, with REST API
POST https://fcm.googleapis.com/fcm/send
Headers
Authorization
key=fcm_key
Content-Type
application/json
Body
raw
{ "notification": { "body": "Notification from postman", "title": "You have a new message (postman).", "android_channel_id": "misc", "click_action": "FLUTTER_NOTIFICATION_CLICK" }, "priority": "high", "data": { "click_action": "FLUTTER_NOTIFICATION_CLICK", "sound": "default", "status": "done", "screen": "AnyScreen" }, "to": "/topics/test"}
Pour tester les notifications push sur les applications en production, vous pouvez modifier "to": "/topics/test"
pour "to": "/topics/new_content"
. Attention, cela enverra la notification à TOUS les appareils utilisant cette application.
POST http://domain.com/api/notifications/send
Accept
application/json
Bearer Token
To get token, refer to top of this guide.
{ "title": "Notification push from Postman", // title of notification "body": "About new_content", // body of notification "android_channel_id": "misc", // optional, only for notification channel on android "to": "/topics/new_content", // FCM topic "all_users_for_this_model": 2, "data": { "click_action": "FLUTTER_NOTIFICATION_CLICK", "sound": "default", "status": "done", "screen": "AnyScreen", "screen_data": 10 }}
Laravel
Actuellement, aucun package n'est utilisé pour envoyer des notifications à FCM, les envois sont faits à travers cURL, dans le NotificationProvider.php
.
Vous aurez besoin d'une clé, nommée Server key, disponible sur la console de Firebase. Celle-ci est dans le .env
de Laravel sur la clé FIREBASE_CLOUD_MESSAGING_SERVER_KEY
référencée dans config/fcm.php
sous le nom server_key
.
TODO:
- en auto: new subscription, new contents
NotificationController.php
app/Http/Controllers/Admin/NotificationController.php
withsend(NotificationRequest $request)
viaNotificationProvider.php
app/Http/Requests/NotificationRequest.php
with list of required argumentsapp/Providers/NotificationProvider.php
withsendPushNotification(array $validated)
to communicate with Cloud Messaging
Routes
routes/api.php
Route::post('/notifications/send', ...);
useful for manual testing with Postmanroutes/web.php
Route::post('notifications/send', ...);
used to automatic sending
Envois automatiques
resources/js/mixins/model.js
withasync onSubmit(refreshData = false)
resources/js/views/admin/real-estate-companies/Edit.vue
withmixins: [model('real-estate-companies', true)],
viamodel.js
resources/js/components/RealEstateCompanyEmailSubscriptionModal.vue
withasync onSubmit()
Packages
Ces packages sont listés ici à titre indicatif, aucun n'est actuellement utilisé.
- https://github.com/EdwinHoksberg/php-fcm
- https://github.com/kreait/laravel-firebase
- https://github.com/brozot/Laravel-FCM
- https://github.com/benwilkins/laravel-fcm-notification
- https://github.com/laravel-notification-channels/fcm
Redirection
TODO:
- redirection after opening notification
Articles
Faire attention aux changements très fréquents dans l'API de Cloud Messaging
- Authenticating Firebase Cloud Messaging HTTP v1 API Requests : ancien mais dédié à l'utilisation de l'API v1
- Testing FCM Push Notification through Postman/Terminal
- Part 1 : détails sur l'API legacy
- Part 2
étails sur l'API v1 - Setup Firebase Cloud Messaging(FCM) push notification : très détaillé, récent
Laravel
<?phpnamespace App\Http\Controllers;use App\Providers\NotificationProvider;use App\Http\Requests\NotificationRequest;class NotificationController extends Controller{ public function send(NotificationRequest $request) { $validated = $request->validated(); if ('production' !== config('app.env')) { $validated['to'] = 'test'; } // Get $data from $request // For postman verification of valid type for data parameter if (isset($request->data)) { $data = $request->data; if ('string' === gettype($request->data)) { $data = json_decode($data, true); } } else { $data = []; } $response = ''; // Send to a group of devices by 'investor_id_#' flutter topic if (isset($validated['all_users_for_this_model'])) { $all_users_for_this_model = $validated['all_users_for_this_model']; if ($validated['to'] = 'new_content') { $user = User::find($all_users_for_this_model); $androidChannel = 'new_content'; } else { return response()->json("Parameter 'to' is not valid for all_users_for_this_model"); } $response = []; foreach ($users->toArray() as $key => $investor) { $validated['to'] = "user_id_{$investor['id']}"; $data['screen_data'] = $user->id; $responseByNotificationProvider = NotificationProvider::sendPushNotification($validated, $data, true, $androidChannel); array_push($response, $responseByNotificationProvider); } // Send to all devices } else { $response = NotificationProvider::sendPushNotification($validated, $data); } return response()->json($response); }}
<?phpnamespace App\Providers;use Illuminate\Http\JsonResponse;use Illuminate\Contracts\Container\BindingResolutionException;class NotificationProvider{ /** * Send push notification to com.group.project.flutter with FCM. * * @throws BindingResolutionException * * @return JsonResponse */ public static function sendPushNotification(array $validated, array $data = [], bool $toTopic = true, string $androidChannel = 'Misc') { $title = $validated['title']; $body = $validated['body']; $to = $validated['to']; if (isset($validated['toTopic'])) { $toTopic = $validated['toTopic']; } if (null === $androidChannel) { $androidChannel = $to; } $url = 'https://fcm.googleapis.com/fcm/send'; $serverKey = config('firebase.cm_server_key'); $notification = [ 'body' => $body, 'title' => $title, 'android_channel_id' => $androidChannel, 'click_action' => 'FLUTTER_NOTIFICATION_CLICK', ]; $priority = 'high'; if ($toTopic) { $to = "/topics/$to"; } $arrayToSend = [ 'notification' => $notification, 'priority' => $priority, 'data' => $data, 'to' => $to, ]; $json = json_encode($arrayToSend); $headers = []; $headers[] = 'Content-Type: application/json'; $headers[] = 'Authorization: key='.$serverKey; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($ch, CURLOPT_POSTFIELDS, $json); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //Send the request $response = curl_exec($ch); //Close request if (false === $response) { exit('FCM Send Error: '.curl_error($ch)); } curl_close($ch); $response = json_decode($response); $response = (array) $response; return [ 'response' => $response, 'notification' => $arrayToSend, ]; } /** * [DEPRECATED] * Create notification from https://documentation.onesignal.com/reference/create-notification. * Push notification for Flutter application by OneSignal. * * @return string|bool */ public static function sendNotificationOneSignal($title, $message, string $appId = 'app-id', string $authorization = 'auth_key') { $content = [ 'en' => $message, ]; $headings = [ 'en' => $title, ]; $subtitle = [ 'en' => 'Subtitle', ]; $hashes_array = []; $fields = [ 'app_id' => $appId, 'included_segments' => [ 'All', ], 'data' => [ 'foo' => 'bar', ], 'contents' => $content, 'headings' => $headings, // 'subtitle' => $subtitle, 'android_background_layout' => 'https://domain.com/images/bg.jpg', 'message_icon' => 'ic_stat_onesignal_default', 'android_accent_color' => 'ffce6442', 'android_channel_id' => 'android_channel_id', 'buttons' => $hashes_array, ]; $fields = json_encode($fields); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://onesignal.com/api/v1/notifications'); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json; charset=utf-8', 'Authorization: Basic '.$authorization, ]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $fields); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $response = curl_exec($ch); curl_close($ch); return [ 'response' => json_decode($response), 'fields' => json_decode($fields), ]; }}