Skip to main content

Class

Opscale\NotificationCenter\Models\Subscription Extends Illuminate\Database\Eloquent\Model. Uses HasUlids, SoftDeletes, and ValidatorTrait. Database table: notification_center_subscriptions

Overview

A Subscription record stores a single verified contact point for a Profile on a specific notification channel. Each subscription has a type (the channel identifier, e.g. 'webpush', 'sms', 'email') and a contact value (the endpoint, phone number, or address for that channel). Multiple subscriptions per profile are supported — a user might have a webpush subscription, an SMS subscription, and an email subscription. A unique constraint on (profile_id, type, contact) prevents duplicates.

Attributes

id
string
required
ULID primary key, auto-generated on creation.
profile_id
string
required
Foreign key referencing notification_center_profiles.id. Cascades on delete.
type
string
required
The notification channel this subscription applies to (e.g. 'webpush', 'email', 'sms'). Matches the channel identifier keys used in config('notification-center.messages').
contact
string
required
The channel-specific contact address or endpoint. For example:
  • webpush — the push subscription endpoint URL registered by the service worker.
  • email — the recipient email address.
  • sms / call / whatsapp — the recipient phone number.
  • nova / card — the primary key of the notifiable user model.
verified
boolean
Whether the contact address has been verified. Defaults to false. Cast to boolean.
priority
integer
Unsigned tiny integer (0–255) indicating dispatch priority when a profile has multiple subscriptions of the same type. Lower values indicate higher priority. Defaults to 5.
created_at
Carbon\Carbon | null
Timestamp of when the subscription was created.
updated_at
Carbon\Carbon | null
Timestamp of the last update.
deleted_at
Carbon\Carbon | null
Soft-delete timestamp. Present when the subscription has been soft-deleted.

Relationships

profile

Returns BelongsTo — the Profile that owns this subscription.
$subscription->profile; // Profile

Unique constraint

The database enforces a composite unique index on (profile_id, type, contact). Attempting to insert a duplicate subscription for the same profile, channel, and contact will throw an integrity constraint violation.

Webpush subscriptions

When a user grants push notification permission in their browser and the service worker registers successfully, the browser provides a push subscription object containing:
  • endpoint — the push service URL (stored in contact)
  • keys (p256dh, auth) — encryption keys used by the push server
The Profile model includes the HasPushSubscriptions trait from laravel-notification-channels/webpush, which handles storing and retrieving these subscriptions.
// JavaScript: registering a push subscription via the service worker
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: urlBase64ToUint8Array(vapidPublicKey),
});

// POST the subscription object to the Notification Center register endpoint
await fetch(`/webpush/register/${profileId}`, {
    method: 'POST',
    body: JSON.stringify(subscription),
    headers: { 'Content-Type': 'application/json' },
});
// PHP: store the webpush subscription on the profile
$profile->updatePushSubscription(
    endpoint: $request->input('endpoint'),
    key:      $request->input('keys.p256dh'),
    token:    $request->input('keys.auth'),
);
updatePushSubscription() is provided by the HasPushSubscriptions trait and will create or update the subscription record automatically, preventing duplicates.

Usage with notification routing

The Profile::routeNotificationFor() method checks whether the dispatched notification implements getSubscription(). If so, it returns that subscription value directly to the channel driver instead of delegating to the default Notifiable routing:
// Profile::routeNotificationFor() — from src/Models/Profile.php
public function routeNotificationFor($driver, $notification = null): mixed
{
    if ($notification && method_exists($notification, 'getSubscription')) {
        return $notification->getSubscription();
    }

    return parent::routeNotificationFor($driver, $notification);
}
All built-in notification classes inherit getSubscription() from the base Notification class, which looks up the matching Subscription record by channel type:
// Base Notification::getSubscription()
public function getSubscription(): mixed
{
    return $this->delivery->profile->subscriptions
        ->where('type', $this->delivery->channel)
        ->first()
        ?->contact;
}

Usage examples

Creating a subscription manually

use Opscale\NotificationCenter\Models\Subscription;

$subscription = Subscription::create([
    'profile_id' => $profile->id,
    'type'       => 'mail',
    'contact'    => '[email protected]',
    'verified'   => true,
    'priority'   => 1,
]);

Querying verified subscriptions for a profile

$verifiedSubscriptions = $profile->subscriptions
    ->where('verified', true)
    ->sortBy('priority');

Removing a webpush subscription on unsubscribe

// Remove a specific push endpoint when the user unsubscribes in the browser
$profile->deletePushSubscription($endpoint);
Soft deletes are enabled on Subscription. Deleting the parent Profile cascades at the database level and permanently removes subscription rows (not soft-deletes). Soft-deleting a profile via Eloquent does not cascade to subscriptions — use explicit cleanup if needed.
Use the priority field to control which subscription is used first when a profile has multiple contact points for the same channel. A value of 1 takes precedence over the default of 5.

Build docs developers (and LLMs) love