← All documentation
Topics

- Working with data
- Designer components
- Field controls
- Search & filtering
- Calendars
- PDF & printing
- Email
- SMS
- Notifications
- Security
- Helper functions
- JavaScript API
- Performance

# Notifications – `Notification` Class & No-Code Rules

The Notification class raises in-app notifications — the little messages that light up the bell in your generated app's navbar. It creates its own table, self-heals its schema, colour-codes messages by severity, and ships targeting helpers so you can notify one user, a role, a group, everyone, or the record's owner in a single line.

There are two ways to send notifications:

1. No-code rules — the Notifications module (project sidebar 🔔). Point-and-click: “When a record is added / edited / deleted, notify these people with this message.” Compiled into event hooks at build time. Recommended for most cases.
2. Code — call the `Notification` API from any Event (afterAdd / afterEdit / …) or custom page when you need full control.

## Contents

- Severity types (colour & icon)
- No-code Notification Rules
- Sending from code — `Add()`
- Targeting helpers (role / group / everyone / owner)
- Reading, counting & marking read
- The bell — badge, animation & toast
- Enabling the table
- Real-world examples

## Severity types

Every notification has a `type`. It sets the icon and accent colour automatically — you never pick an icon unless you want to override it.

`type` | Auto icon | Accent | Use for
--- | --- | --- | ---
`info` (default) | `info-circle` | Blue | General FYI, mentions, assignments
`success` | `check-circle` | Green | Created / approved / completed
`warning` | `exclamation-triangle` | Amber | Needs attention, nearing a limit
`error` | `x-circle` | Red | Failed / rejected / overdue

Pass `'icon' => 'bi-cart'` to override the auto icon. Any Bootstrap-Icons name works.

## No-code Notification Rules

Open the Notifications page in the project sidebar (🔔). Click a one-click template or New rule, then fill in four steps:

Step | Choose
--- | ---
1. When | A table + trigger — record added, edited, or deleted.
2. Notify | Audience — Everyone, a Role, a Group, specific Users, the record owner, or the actor (who did it).
3. Message | Title + message + type. Insert live field values with `{field}` chips.
4. Link | Where clicking the notification goes — the record, nothing, or a custom URL.

Placeholders: put any column of the saved row in curly braces and it is substituted at send time. For an `orders` rule:

    Title:   New order #{id}
    Message: {customer_name} placed an order for {total}

Rules are stored per-project and compiled into the table's afterAdd / afterEdit / afterDelete event hooks at build time — no runtime overhead, and they sit alongside any hand-written event code (they never overwrite it). Enabling any rule automatically provisions the notification bell, its API endpoint, and the table in the generated app.

## Sending from code — `Add()`

    $id = Notification::Add([
        'user_id' => 5,                       // recipient (null = everyone)
        'title'   => 'Orders',
        'message' => 'New order #1234 has been placed.',
        'type'    => 'success',                // info | success | warning | error
        'url'     => '/generated/orders/view.php?id=1234',
        'icon'    => 'bi-cart',                // optional — overrides the type icon
        'expire'  => '2026-12-31 23:59:59',    // optional auto-expiry
    ]);

Key | Meaning
--- | ---
`user_id` | Recipient user id. `null` / omitted = broadcast to all. (`userId` also accepted.)
`title` | Bold heading shown above the message.
`message` | The notification body. Required.
`type` | Severity — drives colour + icon. Defaults to `info`.
`url` | Where clicking navigates. (`link` also accepted.)
`icon` | Override the auto icon (Bootstrap-Icons name).
`expire` | Auto-hide after this timestamp.

You don't have to enable anything first — `Add()` creates the table on demand and self-heals its columns, so the rich fields (title / icon / url / type) work even if a leaner feature (e.g. the Calendar reminders) created the table earlier.

## Targeting helpers

Skip the “look up the user ids” boilerplate — these do it for you and return how many were sent:

Helper | Sends to
--- | ---
`AddForUsers(array $ids, array $data)` | Each id in the list (deduped).
`AddForRole(string $role, array $data)` | Every user whose `role` matches.
`AddForGroup(int $groupId, array $data)` | Every member of the group.
`AddForEveryone(array $data)` | All users (one broadcast row).
`AddForRecordOwner(array $row, string $ownerField, array $data)` | Whoever created the record (reads the id from `$ownerField`).

    // Alert every admin, green tick, links to the record
    Notification::AddForRole('admin', [
        'title'   => 'New order',
        'message' => 'Order #' . $id . ' was placed',
        'type'    => 'success',
        'url'     => '/generated/orders/view.php?id=' . $id,
    ]);

    // Tell the person who created the ticket it was answered
    Notification::AddForRecordOwner($data, 'created_by', [
        'title'   => 'Ticket updated',
        'message' => 'Your ticket "' . $data['subject'] . '" was answered',
        'type'    => 'info',
    ]);

    // Broadcast an announcement to everyone
    Notification::AddForEveryone([
        'title'   => 'Maintenance tonight',
        'message' => 'The app will be read-only from 10–11pm.',
        'type'    => 'warning',
    ]);

## Reading, counting & marking read

    // 20 most recent for the current user
    $items  = Notification::Get(Security::CurrentUserData()['id'], 20);

    // Only unread
    $unread = Notification::Get($userId, 10, true);

    // Unread badge count
    $count  = Notification::Count($userId);

    // Mark one / all read
    Notification::MarkRead(12);
    Notification::MarkAllRead($userId);

    // Housekeeping — drop anything older than 60 days
    Notification::DeleteOld(60);

## The bell — badge, animation & toast

The generated app's navbar bell is wired automatically whenever notifications are in use. It:

- Shows a red badge with the unread count.
- Wiggles when a new message arrives (respects reduced-motion).
- Pops a toast for the newest unread message, colour-matched to its `type`.
- Polls every 30s and refreshes instantly when the tab regains focus.
- Colour-codes each row and shows an accent bar on unread items.

Need to refresh the bell after your own AJAX action? Call `window.ppNotifyRefresh()` on the page.

## Enabling the table

Usually you don't — a Notification rule, a `Notification::Add()` call, or the Calendar's in-app reminders each provision everything on their own. To turn it on explicitly, use Misc → Advanced → Notifications (set the table name, default `notifications`), or programmatically:

    Notification::Enable('notifications');   // create + self-heal the table
    Notification::SetTableName('notifications');

## Real-world examples

### afterAdd — notify admins a customer signed up

    Notification::AddForRole('admin', [
        'title'   => 'New customer',
        'message' => "'{$data['name']}' just registered",
        'type'    => 'success',
        'url'     => '/generated/customers/view.php?id=' . $id,
    ]);

### afterEdit — warn the owner their order shipped

    if (($data['status'] ?? '') === 'shipped') {
        Notification::AddForRecordOwner($data, 'created_by', [
            'title'   => 'Order shipped',
            'message' => 'Order #' . $id . ' is on its way',
            'type'    => 'info',
            'url'     => '/generated/orders/view.php?id=' . $id,
        ]);
    }

### Approaching a limit — a warning to managers

    if ((int)$data['stock'] < 5) {
        Notification::AddForRole('manager', [
            'title'   => 'Low stock',
            'message' => $data['name'] . ' is down to ' . $data['stock'],
            'type'    => 'warning',
        ]);
    }

### 🔗 Related Help Topics

Email API SMS API Calendar Security API