---
title: "Recipient and subject models — Notifications for Laravel | Roundly"
description: "ReceivesNotifications, HasNotificationReminders and the contracts that name a timezone, a group’s members, an owner or a context label."
url: https://roundly-consulting.com/open-source/docs/notifications-for-laravel/recipient-models
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Notifications for Laravel](https://roundly-consulting.com/open-source/docs/notifications-for-laravel.md)

# Recipient and subject models

Two traits and four small contracts connect your models to the package. Each is optional — implement only what your types use.

## ReceivesNotifications — the recipient

Use it on your User next to Laravel’s Notifiable. Every call goes through the manager, so Notifications::fake() sees it:

```php
$user->inbox()->unread()->paginate();               // = Notifications::inbox($user)
$user->notificationPreferences()->mute('comment.posted');   // = Notifications::preferences($user)
$user->unreadNotificationCount();                   // 3
```

## HasNotificationReminders — the subject

Use it on a model whose dates drive reminders (a contract, a task, a trial). Reminders of every type whose subject() is this class — or a parent of it — follow its date attributes:

```php
use RoundlyConsulting\Notifications\Traits\HasNotificationReminders;

class Contract extends Model
{
    use HasNotificationReminders;
}

$contract->update(['expires_at' => now()->addYear()]);   // pending reminders move; sent ones re-arm
$contract->delete();                                      // pending reminders and escalations are cancelled
$contract->restore();                                     // and scheduled again
$contract->notificationReminders();                       // MorphMany<NotificationReminder>
$contract->syncNotificationReminders();                   // int — a manual sync
```

It syncs on created, on updated when an anchor attribute of a registered rule (or of a pending ad-hoc reminder) changed, and on restored — after your commit, so a lock wait or a lost race never fails or rolls back your save() (errors are reported). A delete, soft or force, cancels with subject\_deleted. It is also the subject-lifecycle hook for notifications.inbox.on\_subject\_deleted (keep, archive or delete the subject’s inbox items).

## Contracts

```php
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Notifications\Contracts\HasNotificationContext;
use RoundlyConsulting\Notifications\Contracts\HasNotificationOwner;
use RoundlyConsulting\Notifications\Contracts\HasNotificationTimezone;
use RoundlyConsulting\Notifications\Contracts\ProvidesNotificationRecipients;

// A recipient that names its own timezone — the first source the default resolver asks
class User extends Authenticatable implements HasNotificationTimezone
{
    use Notifiable;
    use ReceivesNotifications;

    public function notificationTimezone(): ?string
    {
        return $this->settings['timezone'] ?? null;
    }
}

// A group behind Recipients::role() / members(), and an inbox context with its own label
class Company extends Model implements ProvidesNotificationRecipients, HasNotificationContext
{
    public function notificationRecipients(?string $role = null): iterable
    {
        return $role === null
            ? $this->users
            : $this->users()->wherePivot('role', $role)->get();
    }

    public function notificationContextLabel(): string
    {
        return $this->legal_name;   // "Acme s.r.o."
    }
}

// A subject that names its owner, for Recipients::owner()
class Contract extends Model implements HasNotificationOwner
{
    use HasNotificationReminders;

    public function notificationOwner(): Model|iterable|null
    {
        return $this->accountManager;
    }
}
```

| Contract | Method | Used by |
| --- | --- | --- |
| `HasNotificationTimezone` | `notificationTimezone(): ?string` | The default timezone resolver — its first source. |
| `ProvidesNotificationRecipients` | `notificationRecipients(?string $role = null): iterable` | The default group resolver behind Recipients::role() and members(). |
| `HasNotificationOwner` | `notificationOwner(): Model\|iterable\|null` | Recipients::owner(). |
| `HasNotificationContext` | `notificationContextLabel(): string` | The label of a model context, else its name attribute, else its basename and key. |
| `RecipientResolver` | `resolve(?Model $subject, array $args): iterable` | Recipients::using(). |
| `ResolvesGroupMembers` | `members(Model $group, ?string $role = null): iterable` | notifications.recipients.group\_resolver. |
| `ResolvesMailAddress` | `address(Model $recipient, ?NotificationPayload $payload): ?string` | notifications.mail.address\_resolver. |
| `ResolvesTimezone` | `timezone(Model $recipient): string` | notifications.timezone.resolver. |

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/notifications-for-laravel.md)
