Reference

API reference

Use the public managers, registry, gate, and value objects.

Notifiable trait

HasNotificationPreferences provides enableNotification, disableNotification, resetNotificationPreference, notificationPreference, effectiveNotificationPreference, and canReceiveNotification.

Preference manager

Use notificationPreferences() for grouped operations:

app/Services/NotificationPreferences.php
$preferences = $user->notificationPreferences();

$preferences->definitions();
$preferences->explicitPreferences();
$preferences->effectivePreferences($context);
$preferences->inspectPreferences($context);
$preferences->for('security.alert', $context)->effective('mail');

The fluent selection also exposes channels, isMandatory, isModifiable, and isHidden.

explicitPreferences(?NotificationContext $context) returns only the explicit preferences for the requested context. Passing null reads global preferences; passing a context authorizes and reads only that context.

inspectPreferences(?NotificationContext $context) returns the available definitions and channels for the requested context. Each NotificationPreferenceInspection entry exposes definition, channel, enabled, reason, source, modifiable, mandatory, and mode. The method uses the same resolver and context authorization as delivery decisions, so an administration or user interface does not need to reproduce the resolution rules.

Context policy manager

Use NotificationContextPreferenceManager for context-owned policies instead of calling the persistence store directly:

$policies = app(NotificationContextPreferenceManager::class);

$policies->get($administrator, $context, 'incident.created', 'mail');
$policies->set($administrator, $context, 'incident.created', 'mail', false);
$policies->enable($administrator, $context, 'incident.created', 'mail', NotificationContextPreferenceMode::ENFORCED);
$policies->disable($administrator, $context, 'incident.created', 'mail');
$policies->reset($administrator, $context, 'incident.created', 'mail');
$policies->inspect($administrator, $context);

The manager authorizes the administrator before every read or write through NotificationContextPolicyAuthorizer. It then verifies that the notification and channel are registered, that the context type is supported by the definition, and that the channel is not mandatory. Its set, enable, and disable methods accept NotificationContextPreferenceMode::DEFAULT or ENFORCED; the enum prevents unsupported modes. inspect returns NotificationContextPolicyInspection entries with the context, definition, channel, stored preference, enabled, mode, configured, modifiable, and mandatory values.

Definition registry

NotificationDefinitionRegistry registers definitions by key and resolves a definition from a notification class. Duplicate keys and missing definitions throw InvalidArgumentException.

Notification gate

NotificationGate::decision() returns a ResolvedPreference for registered notifications. For unknown notifications, the configured unknown_notifications behavior returns null in allow mode, returns a disabled ResolvedPreference with the unknown_notification reason in deny mode, or throws a LogicException in throw mode. allows() follows the same behavior and remains true for unknown notifications only in allow mode. A gate decision uses NotificationDecisionReason for explicit cases such as UNKNOWN_NOTIFICATION, CHANNEL_UNDECLARED, CONTEXT_REQUIRED, CONTEXT_UNSUPPORTED, and CONTEXT_UNAUTHORIZED.

NotificationDeliveryDecided is dispatched synchronously after each non-throwing delivery decision. It contains the notifiable, notification, channel, resolved context, matching definition, and final ResolvedPreference. Unknown notifications also dispatch this event with a null definition; in allow mode, NotificationGate::decision() still returns null to preserve Laravel's normal behavior. The event does not implement ShouldDispatchAfterCommit because it describes a runtime delivery decision rather than a persisted change.

The event exposes customized and originalEnabled when a customizer changes a modifiable decision. Listen to it for application-specific audit logs, metrics, or Telescope integration:

app/Listeners/RecordNotificationDeliveryDecision.php
use NotificationCompass\Events\NotificationDeliveryDecided;

final class RecordNotificationDeliveryDecision
{
    public function handle(NotificationDeliveryDecided $event): void
    {
        logger()->info('Notification delivery decision', [
            'notification' => $event->definition?->key ?? get_class($event->notification),
            'channel' => $event->channel,
            'context' => $event->context?->canonicalKey(),
            'enabled' => $event->preference->enabled,
            'reason' => $event->preference->reason->value,
            'customized' => $event->customized,
        ]);
    }
}

Laravel Telescope's Event watcher can display this event and its payload without any dependency from NotificationCompass. Telescope's Notification watcher remains useful for notifications that are actually delivered; the delivery decision event also covers blocked notifications.

Contracts

Replace persistence with an implementation of NotificationPreferenceStore, or MutableNotificationPreferenceStore when the application needs writes. Implement InspectableNotificationPreferenceStore to expose all explicit preferences.

Implement NotificationContextResolver to discover a context from a notification and notifiable. Implement NotificationContextAuthorizer to validate that the notifiable can access a context:

NotificationContextAuthorizer.php
public function authorize(object $notifiable, NotificationContext $context): bool;

The service provider registers a permissive authorizer by default. A custom implementation can be bound in an application service provider. A denied context raises LogicException when the preference manager reads or writes contextual preferences and produces a disabled ResolvedPreference with the context_unauthorized source when NotificationGate evaluates delivery.

Implement NotificationContextPolicyAuthorizer separately for administrators who create, update, inspect, or reset policies for an entire context:

NotificationContextPolicyAuthorizer.php
public function authorize(object $administrator, NotificationContext $context): bool;

The NotificationContextPreferenceManager passes the administrator and context to this authorizer before every operation. The two authorizers are intentionally independent because membership access and administrative policy access can follow different application rules.

Set notificationcompass.authorization.strict to true to make both default authorizers deny every contextual operation until the application binds its own implementations. This mode prevents an integration from accidentally leaving context authorization permissive.

Implement NotificationDeliveryDecisionCustomizer when the application needs to add a final business rule to modifiable decisions:

NotificationDeliveryDecisionCustomizer.php
public function customize(
    object $notifiable,
    object $notification,
    string $channel,
    ?NotificationContext $context,
    NotificationDefinition $definition,
    ResolvedPreference $preference,
): ?bool;

Return null to keep the resolved value or return a boolean to replace enabled. Bind the implementation in an application service provider. Mandatory decisions, undeclared channels, missing or unsupported contexts, unauthorized contexts, and enforced context policies cannot be overridden by this hook. NotificationCompass does not provide a default business rule.

NotificationContextPreferenceStore reads context-level preferences. Use MutableNotificationContextPreferenceStore when implementing a custom persistence backend. Application code should use NotificationContextPreferenceManager for policy operations:

NotificationContextPreferenceStore.php
public function get(
    NotificationContext $context,
    string $notificationKey,
    string $channel,
): ?NotificationContextPreference;

The default implementation is EloquentNotificationContextPreferenceStore. The manager accepts a NotificationContextPreferenceMode with DEFAULT as the default. DEFAULT lets user preferences take precedence; ENFORCED locks the context policy. The result exposes the selected mode through ResolvedPreference::$mode.

NotificationPreferenceCache caches resolved ResolvedPreference instances. The default LaravelNotificationPreferenceCache uses the configured Laravel cache repository and includes the notifiable identity, notification key, channel, context key, and invalidation versions in each key. Bind NotificationPreferenceCache to a custom implementation when the application needs another cache backend or key strategy.

Definition metadata

NotificationDefinition::$metadata is a NotificationDefinitionMetadata value object with label, description, category, and order properties. NotificationDefinition::channelMetadata($channel) returns a NotificationChannelMetadata value object with the channel label, description, and visible properties. isHidden($channel) returns the inverse of the channel metadata visibility.

These metadata objects are descriptive only. NotificationCompass does not translate labels, select icons, or impose a visual layout.

Context value object

NotificationContext validates a lowercase segmented type, a non-negative integer or safe opaque string id, and a JSON-compatible reference. Numeric string identifiers are normalized before storage. key() and canonicalKey() return the same canonical value, and toArray() provides the structured representation used by JSON and queue serialization.

Decision reasons

ResolvedPreference::$reason is a NotificationDecisionReason enum. Its backed value is stable for logs and tests, while source exposes the same value as a string. The enum distinguishes unknown notifications, mandatory rules, missing, undeclared, unsupported, or unauthorized contexts, user context and global preferences, context policies, type and channel defaults, opt-in defaults, and the package default. Use ResolvedPreference::isDefault() when the interface only needs to know whether the result came from a fallback.

Preference change events

The package dispatches NotificationPreferenceChanged after a preference or context policy is effectively created, modified, reset, or deleted. The event contains the notifiable, context, notification definition, channel, previous value, new value, and a NotificationPreferenceChangeType operation.

NotificationPreferenceChanged implements Laravel's ShouldDispatchAfterCommit. When a Laravel database transaction is active, the event and its listeners are deferred until the outermost transaction commits. A rollback discards the event; outside a transaction, the event runs immediately after persistence. Custom stores using another transaction mechanism must reproduce this guarantee when they dispatch the event.

For a context policy, notifiable is null because the policy belongs to the context itself. The event also exposes oldMode and newMode when the change concerns a default or enforced context policy. Reapplying the same value and mode does not dispatch an event.

Listen to the event using Laravel's normal event system:

app/Listeners/RecordNotificationPreferenceChange.php
use NotificationCompass\Events\NotificationPreferenceChanged;

final class RecordNotificationPreferenceChange
{
    public function handle(NotificationPreferenceChanged $event): void
    {
        // Add application-specific auditing or synchronization here.
    }
}

The package only emits the event. The application decides whether to audit, broadcast, synchronize, or invalidate another cache.