Notifications
Push notifications through OneSignal, backed by a per-user queue that doubles as an in-app inbox — with unread counts, read state and localized copy.
Sending
The Notification class pushes to devices directly. sendToAll() broadcasts to every
registered device, while sendToSome() targets specific device player ids. Each carries a
title, a message, a url to open, and a dataType / dataID pair the app can
route on:
use Framework\Notification\Notification;
// Broadcast to every registered device
Notification::sendToAll(
title: "New release",
message: "Version 2 is out",
url: "changelog",
dataType: "release",
dataID: 42,
);
// Or target specific devices by their player id
Notification::sendToSome(
title: "Order shipped",
message: "Your order is on its way",
url: "orders/7",
dataType: "order",
dataID: 7,
playerIDs: [ "a1b2…", "c3d4…" ],
);
Send results
Delivery records a NotificationResult per user — Sent, or the reason it was skipped:
| Result | Meaning |
|---|---|
Sent | Handed off to the provider. |
InactiveSend | Notifications are turned off (NOTIFICATION_ACTIVE). |
NoDevices | The user has no registered devices. |
ProviderError | The provider rejected the push. |
Configuration
As with email, everything is set through environment values:
| Key | Controls |
|---|---|
NOTIFICATION_ACTIVE | The master switch — off means nothing is sent. |
NOTIFICATION_ICON | The default icon shown on a push. |
NOTIFICATION_USE_ALIAS | Address devices by alias instead of player id. |
NOTIFICATION_LIMIT | Maximum notifications delivered per run. |
NOTIFICATION_DELETE_DAYS | How long delivered notifications are kept. |
The OneSignal provider
Pushes are delivered through OneSignal's REST API, addressed by the device player ids that each device registers when a user signs in. Point it at your OneSignal app with its app id and REST key:
NOTIFICATION_ACTIVE = true
ONESIGNAL_APP_ID = "…"
ONESIGNAL_REST_KEY = "…"The queue & inbox
Most notifications go through the NotificationQueue rather than pushing straight away. It stores one
row per user, which makes it two things at once: a delivery buffer that a cron flushes to each user's devices,
and an in-app inbox with read state and unread counts. Queue one for a credential:
use Framework\Notification\NotificationQueue;
NotificationQueue::add(
credentialID: 42,
currentUser: 1,
title: "Order shipped",
message: "Your order is on its way",
url: "orders/7",
dataType: "order",
dataID: 7,
);
A scheduled sendAll() then looks up each user's devices and pushes the pending rows, while the
rest of the API drives an inbox UI:
| Method | Does |
|---|---|
sendAll() | Cron — push every pending notification to its user's devices. |
getAllForCredential(…) | A user's notifications, for rendering the inbox. |
getUnreadAmount(…) | The unread badge count. |
markAsRead(id) / discard(id) | Mark one as read, or dismiss it. |
deleteOld() | Cron — prune rows past NOTIFICATION_DELETE_DAYS. |
Run the scheduled methods — sendAll() and deleteOld() — from your server's cron.
First expose each as a console command:
use Framework\Discovery\Attr\ConsoleCommand;
use Framework\Notification\NotificationQueue;
class NotificationCommands {
#[ConsoleCommand("sendNotifications")]
public static function send(): void {
NotificationQueue::sendAll();
}
#[ConsoleCommand("cleanNotifications")]
public static function clean(): void {
NotificationQueue::deleteOld();
}
}
Then add the crontab entries:
# crontab
* * * * * cd /srv/app && ./framework sendNotifications
0 4 * * * cd /srv/app && ./framework cleanNotifications
Localized content
Repeated notifications keep their copy in the translation files, exactly like
email content. Each message lives per language under
nls/notifications, keyed by a code, with a title, a message, and a
description — a note for whoever edits the copy:
{
"OrderShipped": {
"description": "Sent when an order ships",
"title": "Order shipped",
"message": "Your order {{orderID}} is on its way"
}
}Add the matching es.json, and at build time the
NotificationBuilder turns each key into a typed NotificationCode, while a
migration loads the copy into the database. Fetch it by language:
use Framework\Notification\NotificationContent;
use Framework\System\NotificationCode;
$content = NotificationContent::get(NotificationCode::OrderShipped, "en");
NotificationContent::render() fills its {{placeholders}} — like
{{orderID}} above — with per-send data before the title and message are queued.