PHPackages                             tobento/service-notifier - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Mail &amp; Notifications](/categories/mail)
4. /
5. tobento/service-notifier

ActiveLibrary[Mail &amp; Notifications](/categories/mail)

tobento/service-notifier
========================

Notifier interface for PHP applications.

2.0.4(3mo ago)0812MITPHPPHP &gt;=8.4

Since Dec 22Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/tobento-ch/service-notifier)[ Packagist](https://packagist.org/packages/tobento/service-notifier)[ Docs](https://www.tobento.ch)[ RSS](/packages/tobento-service-notifier/feed)WikiDiscussions 2.x Synced 3w ago

READMEChangelog (10)Dependencies (44)Versions (12)Used By (2)

Notifier Service
================

[](#notifier-service)

Notifier interface for PHP applications using [Symfony Notifier](https://github.com/symfony/notifier) as default implementation.

Table of Contents
-----------------

[](#table-of-contents)

- [Getting started](#getting-started)
    - [Requirements](#requirements)
    - [Highlights](#highlights)
- [Documentation](#documentation)
    - [Basic Usage](#basic-usage)
        - [Creating And Sending Notifications](#creating-and-sending-notifications)
    - [Notifier](#notifier)
        - [Create Notifier](#create-notifier)
    - [Notifications](#notifications)
        - [Notification](#notification)
        - [Abstract Notification](#abstract-notification)
    - [Recipients](#recipients)
        - [Recipient](#recipient)
        - [Address Recipient](#address-recipient)
        - [User Recipient](#user-recipient)
        - [Guest Recipient](#guest-recipient)
        - [Composite Recipient](#composite-recipient)
    - [Channel](#channel)
        - [Mail Channel](#mail-channel)
            - [Mail Notification](#mail-notification)
            - [Mail Recipient](#mail-recipient)
            - [Mail Channel Factory](#mail-channel-factory)
        - [Sms Channel](#sms-channel)
            - [Sms Notification](#sms-notification)
            - [Sms Recipient](#sms-recipient)
        - [Chat Channel](#chat-channel)
            - [Chat Notification](#chat-notification)
            - [Chat Recipient](#chat-recipient)
            - [Chat Channel Per Recipient](#chat-channel-per-recipient)
        - [Push Channel](#push-channel)
            - [Push Notification](#push-notification)
            - [Push Recipient](#push-recipient)
        - [Storage Channel](#storage-channel)
            - [Storage Notification](#storage-notification)
            - [Storage Recipient](#storage-recipient)
            - [Accessing Storage Notifications](#accessing-storage-notifications)
        - [Browser Channel](#browser-channel)
            - [Browser Notification](#browser-notification)
            - [Browser Recipient](#browser-recipient)
            - [Accessing Browser Notifications](#accessing-browser-notifications)
    - [Channels](#channels)
        - [Default Channels](#default-channels)
        - [Lazy Channels](#lazy-channels)
    - [Queue](#queue)
    - [Events](#events)
- [Credits](#credits)

---

Getting started
===============

[](#getting-started)

Add the latest version of the notifier service project running this command.

```
composer require tobento/service-notifier

```

Requirements
------------

[](#requirements)

- PHP 8.4 or greater

Highlights
----------

[](#highlights)

- Framework-agnostic, will work with any project
- Decoupled design

Documentation
=============

[](#documentation)

Basic Usage
-----------

[](#basic-usage)

### Creating And Sending Notifications

[](#creating-and-sending-notifications)

Once you have [created the notifier](#create-notifier) you can create and send notifications:

```
use Tobento\Service\Notifier\NotifierInterface;
use Tobento\Service\Notifier\Notification;
use Tobento\Service\Notifier\Recipient;

class SomeService
{
    public function send(NotifierInterface $notifier): void
    {
        // Create a Notification that has to be sent:
        // using the "mail" and "sms" channel
        $notification = new Notification(subject: 'New Invoice', channels: ['mail', 'sms'])
            ->content('You got a new invoice for 15 EUR.');

        // The receiver of the notification:
        $recipient = new Recipient(
            email: 'mail@example.com',
            phone: '15556666666',
        );

        // Send the notification to the recipient:
        $notifier->send($notification, $recipient);
    }
}
```

Check out the [Notifications](#notifications) section to learn more about the available notifications you can create or you might create your own notification class fitting your application.

Check out the [Recipients](#recipients) section to learn more about the available recipients you can create or you might create your own recipient class fitting your application.

Notifier
--------

[](#notifier)

### Create Notifier

[](#create-notifier)

```
use Tobento\Service\Notifier\NotifierInterface;
use Tobento\Service\Notifier\Notifier;
use Tobento\Service\Notifier\ChannelsInterface;
use Tobento\Service\Notifier\Channels;
use Tobento\Service\Notifier\QueueHandlerInterface;

$notifier = new Notifier(
    channels: new Channels(), // ChannelsInterface

    // you may set a queue handler if you want to support queuing notifications:
    queueHandler: null, // null|QueueHandlerInterface

    // you may set an event dispatcher if you want to support events:
    eventDispatcher: null, // null|EventDispatcherInterface
);

var_dump($notifier instanceof NotifierInterface);
// bool(true)
```

Check out the [Channels](#channels) section to learn more about the available channels.

Notifications
-------------

[](#notifications)

### Notification

[](#notification)

The `Notification::class` may be used to create simple notification messages supporting all [channels](#channel).

```
use Tobento\Service\Notifier\Notification;

$notification = new Notification(
    subject: 'New Invoice',
    content: 'You got a new invoice for 15 EUR.',
    channels: ['mail', 'sms'],
);
```

If you want to support custom channels you may consider creating a custom notification!

**Available methods**

```
use Tobento\Service\Notifier\Notification;

$notification = new Notification()
    // you may prefer using the subject method:
    ->subject('New Invoice')
    // you may prefer using the content method:
    ->content('You got a new invoice for 15 EUR.')
    // you may specify a name for any later usage:
    ->name('New Invoice');
```

In addition, you may add messages for specific channels:

```
use Tobento\Service\Notifier\Notification;
use Tobento\Service\Notifier\Message;

$notification = new Notification(
    subject: 'General subject used if no specific message',
    channels: ['mail', 'sms'],
)
->addMessage('sms', new Message\Sms(
    subject: 'Specific sms message',
))
// or specific sms channel:
->addMessage('sms/vonage', new Message\Sms(
    subject: 'Specific sms message',
));
```

### Abstract Notification

[](#abstract-notification)

Use the `AbstractNotification::class` if you want to create specific messages for each channel.

Simply extend from the `AbstractNotification::class` and add the message interfaces with its method you want to support.

Furthermore, any `to` methods such as `toSms` will receive a `$recipient` entity, the `$channel` name and you may request any service being resolved (autowired) by the container.

```
use Tobento\Service\Notifier\AbstractNotification;
use Tobento\Service\Notifier\RecipientInterface;
use Tobento\Service\Notifier\Message;

class OrderNotification extends AbstractNotification implements Message\ToSms
{
    /**
     * Create an order notification.
     *
     * @param Order $order
     */
    public function __construct(
        protected Order $order,
    ) {}

    /**
     * Returns the sms message.
     *
     * @param RecipientInterface $recipient
     * @param string $channel The channel name.
     * @return Message\SmsInterface
     */
    public function toSms(RecipientInterface $recipient, string $channel, SomeService $service): Message\SmsInterface
    {
        return new Message\Sms(
            subject: sprintf('Thanks for your order %s', $this->order->name),
        );
    }
}
```

Recipients
----------

[](#recipients)

### Recipient

[](#recipient)

The `Recipient::class` may be used to create a recipient supporting all [channels](#channel).

```
use Tobento\Service\Notifier\Recipient;

$recipient = new Recipient(
    email: 'mail@example.com', // null|string
    phone: '15556666666', // null|string
    id: 'unique-id', // null|string|int
    type: 'users', // null|string
    locale: 'en', // string (en default)
    // you may set the channels the recipient prefers:
    channels: [],
);

// you may add specific addresses:
$recipient->addAddress(
    channel: 'chat/slack',
    address: ['key' => 'value'] // mixed
);
```

### Address Recipient

[](#address-recipient)

The `AddressRecipient::class` may be used if you have installed the [User Service](https://github.com/tobento-ch/service-user).

```
use Tobento\Service\Notifier\AddressRecipient;
use Tobento\Service\User\Address;

$recipient = new AddressRecipient(
    address: new Address(
        key: 'primary',
        email: 'john@example.com',
        smartphone: '+111222333444',

        // you may define chat channel addresses:
        meta: [
            'channel_addresses' => [
                'chat/slack' => 'slack://TOKEN@default?channel=CHANNEL',
            ],
        ],
    ),
    channels: [],
);
```

### User Recipient

[](#user-recipient)

The `UserRecipient::class` may be used if you have installed the [User Service](https://github.com/tobento-ch/service-user).

```
use Tobento\Service\Notifier\UserRecipient;
use Tobento\Service\User\User;

$recipient = new UserRecipient(
    user: new User(
        email: 'john@example.com',
        smartphone: '+111222333444',

        // you may define chat channel addresses:
        meta: [
            'channel_addresses' => [
                'chat/slack' => 'slack://TOKEN@default?channel=CHANNEL',
            ],
        ],
    ),
    channels: [],
);
```

### Guest Recipient

[](#guest-recipient)

The `GuestRecipient::class` represents a recipient that does not necessarily have a persistent user identity.

It is useful for scenarios such as:

- anonymous visitors
- temporary browser sessions
- browser-specific identifiers (cookie, session, token, etc.)
- broadcast-style notifications
- any custom identification strategy your application defines

Guest recipients behave like any other recipient but offer additional flexibility for browser-based or temporary notification delivery.

```
use Tobento\Service\Notifier\GuestRecipient;
use DateInterval;

// Guest recipient example
$recipient = new GuestRecipient(
    id: null,
    // or:
    // id: 'unique-id', // e.g. cookie, session, token, etc.

    // Expiration (optional):
    expiresAfter: 3600, // seconds
    // or:
    // expiresAfter: new DateInterval('PT2H'),

    // Preferred locale (optional, default: 'en'):
    locale: 'en',

    // Preferred channels (optional):
    channels: ['browser/database'],
);
```

The `id` may be:

- `null` - meaning the notification is not tied to a specific user
- a string or integer - if your application assigns a temporary or custom identifier

The Notifier does not enforce a specific meaning for the ID.
You are free to choose how guests are identified in your application.

### Composite Recipient

[](#composite-recipient)

The `CompositeRecipient::class` may be used to compose the recipient from multiple recipients. The first found address will be used.

```
use Tobento\Service\Notifier\AddressRecipient;
use Tobento\Service\Notifier\CompositeRecipient;
use Tobento\Service\Notifier\Recipient;

$recipient = new CompositeRecipient(
    new AddressRecipient(address: $address),
    new Recipient()->addAddress(
        channel: 'chat/slack',
        address: ['key' => 'value']
    ),
);
```

Channel
-------

[](#channel)

### Mail Channel

[](#mail-channel)

The mail channel uses the [Mail Service](https://github.com/tobento-ch/service-mail) to send notifications.

```
use Tobento\Service\Notifier\Mail;
use Tobento\Service\Notifier\ChannelInterface;
use Tobento\Service\Mail\MailerInterface;
use Psr\Container\ContainerInterface;

$channel = new Mail\Channel(
    name: 'mail',
    mailer: $mailer, // MailerInterface
    container: $container, // ContainerInterface
);

var_dump($channel instanceof ChannelInterface);
// bool(true)
```

#### Mail Notification

[](#mail-notification)

To send mail notifications you have multiple options:

**Using the [Abstract Notification](#abstract-notification)**

Simply extend from the `AbstractNotification::class` and implement the `ToMail` interface. The interface requires a `toMailHandler` method which is already added on the `AbstractNotification::class` defining the `toMail` method as the message handler. You will just need to add the `toMail` method which will receive a `$recipient` entity, the `$channel` name and you may request any service being resolved (autowired) by the container.

```
use Tobento\Service\Notifier\AbstractNotification;
use Tobento\Service\Notifier\RecipientInterface;
use Tobento\Service\Notifier\Message\ToMail;
use Tobento\Service\Mail\Message;

class SampleNotification extends AbstractNotification implements ToMail
{
    /**
     * Returns the mail message.
     *
     * @param RecipientInterface $recipient
     * @param string $channel The channel name.
     * @return Message
     */
    public function toMail(RecipientInterface $recipient, string $channel, SomeService $service): Message
    {
        return new Message()
            // not required if none is defined, the address will be added on sending:
            ->to('to@example.com')

            ->subject('Subject')
            //->textTemplate('welcome-text')
            //->htmlTemplate('welcome')
            //->text('Lorem Ipsum')
            ->html('Lorem Ipsum');
    }
}
```

If you do not have defined a [default from address](https://github.com/tobento-ch/service-mail#default-addresses-and-parameters), you will need to set it on each message:

```
use Tobento\Service\Mail\Message;

$message = new Message()
    ->from('from@example.com');
```

**Using the [Notification](#notification)**

```
use Tobento\Service\Notifier\Notification;
use Tobento\Service\Mail;

$notification = new Notification(
    subject: 'New Invoice',
    content: 'You got a new invoice for 15 EUR.',
);

// with specific mail message:
$notification = new Notification()
    ->addMessage('mail', new Mail\Message()
        ->subject('Subject')
        ->html('Lorem Ipsum')
    );
```

Check out the [Mail Message](https://github.com/tobento-ch/service-mail#message) section to learn more about mail messages.

#### Mail Recipient

[](#mail-recipient)

When sending notifications via the mail channel, the channel will call the `getAddressForChannel` method on the recipient entity to get the email address when no were defined on the mail message.

```
use Tobento\Service\Notifier\Recipient;
use Tobento\Service\Notifier\Address;
use Tobento\Service\Notifier\Notification;

$recipient = new Recipient(
    email: 'mail@example.com',
    // or
    email: new Address\Email('mail@example.com', 'Name'),
);

$address = $recipient->getAddressForChannel('mail', new Notification('subject'));

var_dump($address instanceof Address\EmailInterface);
// bool(true)
```

#### Mail Channel Factory

[](#mail-channel-factory)

```
use Tobento\Service\Notifier\Mail\ChannelFactory;
use Tobento\Service\Notifier\ChannelInterface;
use Tobento\Service\Mail\MailerInterface;
use Psr\Container\ContainerInterface;

$factory = new ChannelFactory(
    mailer: $mailer, // MailerInterface
    container: $container, // ContainerInterface
);

$channel = $factory->createChannel(name: 'mail');

// using a specific mailer:
$channel = $factory->createChannel(name: 'mail/mailchimp', config: [
    'mailer' => 'mailchimp',
]);

var_dump($channel instanceof ChannelInterface);
// bool(true)
```

### Sms Channel

[](#sms-channel)

Use the `ChannelAdapter::class` to create a SMS channel using the [Symfony SMS Channel](https://symfony.com/doc/current/notifier.html#sms-channel):

You will need to install any chat service you would like `composer require symfony/vonage-notifier` e.g.

```
use Tobento\Service\Notifier\Symfony\ChannelAdapter;
use Tobento\Service\Notifier\ChannelInterface;
use Psr\Container\ContainerInterface;

$channel = new ChannelAdapter(
    name: 'sms/vonage',
    channel: new \Symfony\Component\Notifier\Channel\SmsChannel(
        transport: new \Symfony\Component\Notifier\Bridge\Vonage\VonageTransport(
            apiKey: '******',
            apiSecret: '******',
            from: 'FROM',
        )
    ),
    container: $container, // ContainerInterface
);

var_dump($channel instanceof ChannelInterface);
// bool(true)
```

#### Sms Notification

[](#sms-notification)

To send SMS notifications you have multiple options:

**Using the [Abstract Notification](#abstract-notification)**

Simply extend from the `AbstractNotification::class` and implement the `ToSms` interface. The interface requires a `toSmsHandler` method which is already added on the `AbstractNotification::class` defining the `toSms` method as the message handler. You will just need to add the `toSms` method which will receive a `$recipient` entity, the `$channel` name and you may request any service being resolved (autowired) by the container.

```
use Tobento\Service\Notifier\AbstractNotification;
use Tobento\Service\Notifier\RecipientInterface;
use Tobento\Service\Notifier\Message;

class SampleNotification extends AbstractNotification implements Message\ToSms
{
    /**
     * Returns the sms message.
     *
     * @param RecipientInterface $recipient
     * @param string $channel The channel name.
     * @return Message\SmsInterface
     */
    public function toSms(RecipientInterface $recipient, string $channel): Message\SmsInterface
    {
        return new Message\Sms(
            subject: 'Sms message',

            // optionally, you can override default "from" defined in channel
            from: '+1422222222',
        );

        // you may set a specific to address:
        return new Message\Sms(
            subject: 'Sms message',
            to: $recipient->getAddressForChannel('sms/vonage'),
        );
    }
}
```

**Using the [Notification](#notification)**

```
use Tobento\Service\Notifier\Notification;
use Tobento\Service\Notifier\Message;

$notification = new Notification(
    subject: 'New Invoice',
    content: 'You got a new invoice for 15 EUR.',
);

// with specific sms message:
$notification = new Notification()
    ->addMessage('sms', new Message\Sms(
        subject: 'Sms message',
    ));
```

#### Sms Recipient

[](#sms-recipient)

When sending notifications via the sms channel, the channel will call the `getAddressForChannel` method on the recipient entity to get the sms address when no specific were defined.

```
use Tobento\Service\Notifier\Recipient;
use Tobento\Service\Notifier\Address;
use Tobento\Service\Notifier\Notification;

$recipient = new Recipient(
    phone: '15556666666',
    // or
    phone: new Address\Phone('15556666666', 'Name'),
);

$address = $recipient->getAddressForChannel('sms', new Notification('subject'));

var_dump($address instanceof Address\PhoneInterface);
// bool(true)
```

### Chat Channel

[](#chat-channel)

Use the `ChannelAdapter::class` to create a chat channel using the [Symfony Chat Channel](https://symfony.com/doc/current/notifier.html#chat-channel):

You will need to install any chat service you would like `composer require symfony/slack-notifier` e.g.

```
use Tobento\Service\Notifier\Symfony\ChannelAdapter;
use Tobento\Service\Notifier\ChannelInterface;
use Psr\Container\ContainerInterface;

$channel = new ChannelAdapter(
    name: 'chat/slack',
    channel: new \Symfony\Component\Notifier\Channel\ChatChannel(
        transport: new \Symfony\Component\Notifier\Bridge\Slack\SlackTransport(
            accessToken: '******',
        )
    ),
    container: $container, // ContainerInterface
);

var_dump($channel instanceof ChannelInterface);
// bool(true)
```

#### Chat Notification

[](#chat-notification)

To send chat notifications you have multiple options:

**Using the [Abstract Notification](#abstract-notification)**

Simply extend from the `AbstractNotification::class` and implement the `ToChat` interface. The interface requires a `toChatHandler` method which is already added on the `AbstractNotification::class` defining the `toChat` method as the message handler. You will just need to add the `toChat` method which will receive a `$recipient` entity, the `$channel` name and you may request any service being resolved (autowired) by the container.

```
use Tobento\Service\Notifier\AbstractNotification;
use Tobento\Service\Notifier\RecipientInterface;
use Tobento\Service\Notifier\Message;
use Tobento\Service\Notifier\Symfony\MessageOptions;
use Symfony\Component\Notifier\Bridge\Slack\SlackOptions;

class SampleNotification extends AbstractNotification implements Message\ToChat
{
    /**
     * Returns the chat message.
     *
     * @param RecipientInterface $recipient
     * @param string $channel The channel name.
     * @return Message\ChatInterface
     */
    public function toChat(RecipientInterface $recipient, string $channel): Message\ChatInterface
    {
        if ($channel === 'chat/slack') {
            // you may set message options:
            $options = new SlackOptions();

            return new Message\Chat('Chat message')
                ->parameter(new MessageOptions($options));
        }

        // for any other chat channel:
        return new Message\Chat(
            subject: 'Chat message',
        );
    }
}
```

**Using the [Notification](#notification)**

```
use Tobento\Service\Notifier\Notification;
use Tobento\Service\Notifier\Message;

$notification = new Notification(
    subject: 'New Invoice',
    content: 'You got a new invoice for 15 EUR.',
);

// with specific chat message:
$notification = new Notification()
    ->addMessage('chat/slack', new Message\Chat(
        subject: 'Chat message',
    ));
```

#### Chat Recipient

[](#chat-recipient)

When sending notifications via the chat channel, you may add a specific channel address with parameters for later usage.

```
use Tobento\Service\Notifier\Recipient;
use Tobento\Service\Notifier\Address;
use Tobento\Service\Notifier\Notification;

$recipient = new Recipient()
    ->addAddress('chat/slack', ['channel' => 'name']);

$address = $recipient->getAddressForChannel('chat/slack', new Notification('subject'));

var_dump($address);
// array(1) { ["channel"]=> string(4) "name" }
```

**Sample notification with address usage**

```
use Tobento\Service\Notifier\AbstractNotification;
use Tobento\Service\Notifier\RecipientInterface;
use Tobento\Service\Notifier\Message;
use Tobento\Service\Notifier\Symfony\MessageOptions;
use Symfony\Component\Notifier\Bridge\Slack\SlackOptions;

class SampleNotification extends AbstractNotification implements Message\ToChat
{
    /**
     * Returns the chat message.
     *
     * @param RecipientInterface $recipient
     * @param string $channel The channel name.
     * @return Message\ChatInterface
     */
    public function toChat(RecipientInterface $recipient, string $channel): Message\ChatInterface
    {
        if ($channel === 'chat/slack') {
            $address = $recipient->getAddressForChannel('chat/slack', $this);

            $options = new SlackOptions([
                'recipient_id' => $address['channel'] ?? null,
            ]);

            return new Message\Chat('Chat message')
                ->parameter(new MessageOptions($options));
        }

        // for any other chat channel:
        return new Message\Chat(
            subject: 'Chat message',
        );
    }
}
```

#### Chat Channel Per Recipient

[](#chat-channel-per-recipient)

When delivering notifications through the chat channel, you can assign a dedicated chat address to each recipient:

```
use Tobento\Service\Notifier\Recipient;
use Tobento\Service\Notifier\Address;
use Tobento\Service\Notifier\Notification;

$recipient = new Recipient()
    ->addAddress(
        channel: 'chat/slack',
        address: new Address\Dsn('slack://TOKEN@default?channel=CHANNEL'),
    );

$address = $recipient->getAddressForChannel('chat/slack', new Notification('subject'));

var_dump($address);
// object(Tobento\Service\Notifier\Address\Dsn)#16 (2) { ["dsn":protected]=> string(37) "slack://TOKEN@default?channel=CHANNEL" ["name":protected]=> NULL }
```

**Channel Configuration**

You can configure the `PerRecipientChatChannel::class` to ensure that a message is only sent if the recipient has a chat address defined. If no address exists, you may specify an alternative chat channel to handle delivery:

```
use Tobento\Service\Notifier\Symfony\ChannelAdapter;
use Tobento\Service\Notifier\Symfony\PerRecipientChatChannel;
use Tobento\Service\Notifier\ChannelInterface;
use Psr\Container\ContainerInterface;

$channel = new ChannelAdapter(
    name: 'chat/slack',
    channel: new PerRecipientChatChannel(),
    /*channel: new \Symfony\Component\Notifier\Channel\ChatChannel(
        transport: new \Symfony\Component\Notifier\Bridge\Slack\SlackTransport(
            accessToken: '******',
        )
    ),*/
    container: $container, // ContainerInterface
);

var_dump($channel instanceof ChannelInterface);
// bool(true)
```

### Push Channel

[](#push-channel)

Use the `ChannelAdapter::class` to create a push channel using the [Symfony Push Channel](https://symfony.com/doc/current/notifier.html#push-channel):

You will need to install any push service you would like `composer require symfony/one-signal-notifier` e.g.

```
use Tobento\Service\Notifier\Symfony\ChannelAdapter;
use Tobento\Service\Notifier\ChannelInterface;
use Psr\Container\ContainerInterface;

$channel = new ChannelAdapter(
    name: 'push/one-signal',
    channel: new \Symfony\Component\Notifier\Channel\ChatChannel(
        transport: new \Symfony\Component\Notifier\Bridge\OneSignal\OneSignalTransport(
            appId: '******',
            apiKey: '******',
        )
    ),
    container: $container, // ContainerInterface
);

var_dump($channel instanceof ChannelInterface);
// bool(true)
```

#### Push Notification

[](#push-notification)

To send push notifications you have multiple options:

**Using the [Abstract Notification](#abstract-notification)**

Simply extend from the `AbstractNotification::class` and implement the `ToPush` interface. The interface requires a `toPushHandler` method which is already added on the `AbstractNotification::class` defining the `toPush` method as the message handler. You will just need to add the `toPush` method which will receive a `$recipient` entity, the `$channel` name and you may request any service being resolved (autowired) by the container.

```
use Tobento\Service\Notifier\AbstractNotification;
use Tobento\Service\Notifier\RecipientInterface;
use Tobento\Service\Notifier\Message;
use Tobento\Service\Notifier\Symfony\MessageOptions;
use Symfony\Component\Notifier\Bridge\OneSignal\OneSignalOptions;

class SampleNotification extends AbstractNotification implements Message\ToPush
{
    /**
     * Returns the push message.
     *
     * @param RecipientInterface $recipient
     * @param string $channel The channel name.
     * @return Message\PushInterface
     */
    public function toPush(RecipientInterface $recipient, string $channel): Message\PushInterface
    {
        if ($channel === 'push/one-signal') {
            // you may set message options:
            $options = new OneSignalOptions([]);

            return new Message\Push('Push subject')
                ->content('Push content')
                ->parameter(new MessageOptions($options));
        }

        // for any other push channel:
        return new Message\Push(
            subject: 'Push subject',
            content: 'Push content',
        );
    }
}
```

**Using the [Notification](#notification)**

```
use Tobento\Service\Notifier\Notification;
use Tobento\Service\Notifier\Message;

$notification = new Notification(
    subject: 'New Invoice',
    content: 'You got a new invoice for 15 EUR.',
);

// with specific chat message:
$notification = new Notification()
    ->addMessage('push/one-signal', new Message\Push(
        subject: 'Push subject',
        content: 'Push content',
    ));
```

#### Push Recipient

[](#push-recipient)

When sending notifications via the push channel, you may add a specific channel address with parameters for later usage.

```
use Tobento\Service\Notifier\Recipient;
use Tobento\Service\Notifier\Address;
use Tobento\Service\Notifier\Notification;

$recipient = new Recipient()
    ->addAddress('push/one-signal', ['recipient_id' => 'id']);

$address = $recipient->getAddressForChannel('push/one-signal', new Notification('subject'));

var_dump($address);
// array(1) { ["recipient_id"]=> string(2) "id" }
```

**Sample notification with address usage**

```
use Tobento\Service\Notifier\AbstractNotification;
use Tobento\Service\Notifier\RecipientInterface;
use Tobento\Service\Notifier\Message;
use Tobento\Service\Notifier\Symfony\MessageOptions;
use Symfony\Component\Notifier\Bridge\OneSignal\OneSignalOptions;

class SampleNotification extends AbstractNotification implements Message\ToPush
{
    /**
     * Returns the push message.
     *
     * @param RecipientInterface $recipient
     * @param string $channel The channel name.
     * @return Message\PushInterface
     */
    public function toPush(RecipientInterface $recipient, string $channel): Message\PushInterface
    {
        if ($channel === 'push/one-signal') {
            $address = $recipient->getAddressForChannel('push/one-signal', $this);

            $options = new OneSignalOptions([
                'recipient_id' => $address['recipient_id'] ?? null,
            ]);

            return new Message\Push('Push subject')
                ->content('Push content')
                ->parameter(new MessageOptions($options));
        }

        // for any other push channel:
        return new Message\Push(
            subject: 'Push subject',
            content: 'Push content',
        );
    }
}
```

### Storage Channel

[](#storage-channel)

The storage channel stores the notification information in the configured storage repository.

```
use Tobento\Service\Notifier\Storage;
use Tobento\Service\Notifier\ChannelInterface;
use Tobento\Service\Repository\RepositoryInterface;
use Psr\Container\ContainerInterface;

$channel = new Storage\Channel(
    name: 'storage/database',
    repository: $repository, // RepositoryInterface
    container: $container, // ContainerInterface
);

var_dump($channel instanceof ChannelInterface);
// bool(true)
```

Check out the [Repository Service](https://github.com/tobento-ch/service-repository) to learn more about it.

The storage needs to have the following table columns:

ColumnTypeDescription`id`bigint(21) primary key-`name`varchar(255)Used to store the notification name`recipient_id`varchar(36)Used to store the recipient id`recipient_type`varchar(255)Used to store the recipient type`data`jsonUsed to store the message data`read_at`datetimeUsed to store date read at`created_at`datetimeUsed to store date created at**Storage Repository**

You may use the provided `StorageRepository::class` as the repository implementation for storing notifications.

To use it, install the storage repository service:

`composer require tobento/service-repository-storage`

```
use Tobento\Service\Notifier\Storage;
use Tobento\Service\Notifier\ChannelInterface;
use Tobento\Service\Repository\RepositoryInterface;
use Tobento\Service\Storage\StorageInterface;
use Psr\Container\ContainerInterface;

$channel = new Storage\Channel(
    name: 'storage/database',
    repository: new Storage\StorageRepository(
        storage: $storage, // StorageInterface
        table: 'notifications',
    ),
    container: $container, // ContainerInterface
);
```

Check out the [Storage Service - Storages](https://github.com/tobento-ch/service-storage#storages) for the available storages.

For more details about repository usage, see the [Repository Storage Service](https://github.com/tobento-ch/service-repository-storage).

#### Storage Notification

[](#storage-notification)

To send Storage notifications you have multiple options:

**Using the [Abstract Notification](#abstract-notification)**

Extend the `AbstractNotification::class` and implement the `ToStorage` interface.
The interface requires a `toStorageHandler` method, which is already provided by `AbstractNotification`.

You only need to implement the `toStorage` method, which receives:

- the `$recipient` entity
- the `$channel` name
- any autowired services you need

This method must return a `Message\StorageInterface` instance.

```
use Tobento\Service\Notifier\AbstractNotification;
use Tobento\Service\Notifier\RecipientInterface;
use Tobento\Service\Notifier\Message;

class SampleNotification extends AbstractNotification implements Message\ToStorage
{
    /**
     * Returns the storage message.
     *
     * @param RecipientInterface $recipient
     * @param string $channel The channel name.
     * @return Message\StorageInterface
     */
    public function toStorage(RecipientInterface $recipient, string $channel): Message\StorageInterface
    {
        return new Message\Storage(data: [
            'order_id' => $this->order->id,
        ]);
    }
}
```

**Using the [Notification](#notification)**

```
use Tobento\Service\Notifier\Notification;
use Tobento\Service\Notifier\Message;

$notification = new Notification(
    subject: 'New Invoice',
    content: 'You got a new invoice for 15 EUR.',
);

// with specific storage message:
$notification = new Notification()
    ->addMessage('storage', new Message\Storage([
        'foo' => 'bar',
    ]));
```

#### Storage Recipient

[](#storage-recipient)

When sending notifications via the Storage Channel, the channel stores the recipient information using the values returned by `$recipient->getId()` and `$recipient->getType()`.
These values allow you to later fetch or filter notifications for any recipient type your application defines, such as users, system entities, or custom recipient groups.

```
// The channel stores the following data when sending:
$repository->create([
    'name' => $notification->getName(),
    'recipient_id' => $recipient->getId(),
    'recipient_type' => $recipient->getType(),
    'data' => $message->getData(),
    'read_at' => null,
    'created_at' => null,
]);
```

#### Accessing Storage Notifications

[](#accessing-storage-notifications)

Once notifications are stored, you can retrieve the notifications using the repository from the channel:

```
$channel = $channels->get(name: 'storage/database');

$entities = $channel->repository()->findAll(where: [
    'recipient_id' => $userId,
    //'recipient_type' => 'user',
]);
```

### Browser Channel

[](#browser-channel)

The Browser Channel stores notification messages in the configured repository so they can later be delivered to the browser (for example via SSE).

```
use Psr\Clock\ClockInterface;
use Psr\Container\ContainerInterface;
use Tobento\Service\Notifier\Browser;
use Tobento\Service\Notifier\ChannelInterface;
use Tobento\Service\Repository\RepositoryInterface;

$channel = new Browser\Channel(
    name: 'browser/database',
    repository: $repository, // RepositoryInterface
    clock: $clock, // ClockInterface
    container: $container, // ContainerInterface
);

var_dump($channel instanceof ChannelInterface);
// bool(true)
```

Check out the [Repository Service](https://github.com/tobento-ch/service-repository) to learn more about it.

The storage needs to have the following table columns:

ColumnTypeDescription`id`bigint(21) primary key-`name`varchar(255)Used to store the notification name`recipient_id`varchar(36)Used to store the recipient id`recipient_type`varchar(255)Used to store the recipient type`data`jsonUsed to store the message data`expires_at`datetimeStores the exact date and time when the notification becomes invalid and should no longer be shown to the recipient.`read_at`datetimeUsed to store date read at`created_at`datetimeUsed to store date created at**Browser Repository**

You may use the provided `StorageRepository::class` as the repository implementation for storing browser notifications.

To use it, install the storage repository service:

`composer require tobento/service-repository-storage`

```
use Psr\Clock\ClockInterface;
use Psr\Container\ContainerInterface;
use Tobento\Service\Notifier\Browser;
use Tobento\Service\Notifier\ChannelInterface;
use Tobento\Service\Repository\RepositoryInterface;
use Tobento\Service\Storage\StorageInterface;

$channel = new Browser\Channel(
    name: 'browser/database',
    repository: new Browser\StorageRepository(
        storage: $storage, // StorageInterface
        table: 'browser_notifications',
    ),
    clock: $clock, // ClockInterface
    container: $container, // ContainerInterface
);
```

Check out the [Storage Service - Storages](https://github.com/tobento-ch/service-storage#storages) for the available storages.

For more details about repository usage, see the [Repository Storage Service](https://github.com/tobento-ch/service-repository-storage).

#### Browser Notification

[](#browser-notification)

To send Browser notifications you have multiple options.

**Using the [Abstract Notification](#abstract-notification)**

Extend the `AbstractNotification::class` and implement the `ToBrowser` interface.
The interface requires a `toBrowserHandler` method, which is already provided by `AbstractNotification`.

You only need to implement the `toBrowser` method, which receives:

- the `$recipient` entity
- the `$channel` name
- any autowired services you need

This method must return a `Message\BrowserInterface` instance.

```
use Tobento\Service\Notifier\AbstractNotification;
use Tobento\Service\Notifier\RecipientInterface;
use Tobento\Service\Notifier\Message;

class SampleNotification extends AbstractNotification implements Message\ToBrowser
{
    /**
     * Returns the browser message.
     *
     * @param RecipientInterface $recipient
     * @param string $channel The channel name.
     * @return Message\BrowserInterface
     */
    public function toBrowser(RecipientInterface $recipient, string $channel): Message\BrowserInterface
    {
        return new Message\Browser(data: [
            'order_id' => $this->order->id,
        ]);
    }
}
```

**Using the [Notification](#notification)**

```
use Tobento\Service\Notifier\Notification;
use Tobento\Service\Notifier\Message;

$notification = new Notification(
    subject: 'New Invoice',
    content: 'You got a new invoice for 15 EUR.',
);

// with specific browser message:
$notification = new Notification()
    ->addMessage('browser', new Message\Browser([
        'foo' => 'bar',
    ]));
```

#### Browser Recipient

[](#browser-recipient)

When sending notifications via the Browser Channel, the channel stores the recipient information using the values returned by `$recipient->getId()` and `$recipient->getType()`.
These values allow you to later fetch or filter notifications for a specific user, guest, browser ID, or any other recipient type your application defines.

```
// The channel stores the following data when sending:
$repository->create([
    'name' => $notification->getName(),
    'recipient_id' => $recipient->getId(),
    'recipient_type' => $recipient->getType(),
    'data' => $message->getData(),
    'expires_at' => null,
    'read_at' => null,
    'created_at' => null,
]);
```

**GuestRecipient**

A GuestRecipient may be used when sending notifications to anonymous or temporary browser recipients.
If the recipient is a `GuestRecipient` and defines an `expiresAfter` value (either an integer in seconds or a `DateInterval`), the Browser Channel will automatically calculate and store the `expires_at` timestamp using the injected `ClockInterface`.

```
use Tobento\Service\Notifier\GuestRecipient;

// Guest recipient with 1-hour expiration
$recipient = new GuestRecipient(
    id: null,
    expiresAfter: 3600,
);
```

When the `id` is `null`, the notification is not tied to a specific user. How this is interpreted is up to your application. For example, you may use it for broadcast-style notifications or identify the browser using a cookie or session value.

For more detail see the [Guest Recipient](#guest-recipient) section.

#### Accessing Browser Notifications

[](#accessing-browser-notifications)

Once notifications are stored, you can retrieve them using the repository provided by the channel:

```
$channel = $channels->get(name: 'browser/database');

$entities = $channel->repository()->findAll(where: [
    'recipient_id' => $userId,
    //'recipient_type' => 'user',
]);
```

Channels
--------

[](#channels)

### Default Channels

[](#default-channels)

```
use Tobento\Service\Notifier\Channels;
use Tobento\Service\Notifier\ChannelsInterface;
use Tobento\Service\Notifier\ChannelInterface;

$channels = new Channels(
    $channel, // ChannelInterface
    $anotherChannel, // ChannelInterface
);

var_dump($channels instanceof ChannelsInterface);
// bool(true)
```

### Lazy Channels

[](#lazy-channels)

The `LazyChannels::class` creates the channels only on demand.

```
use Tobento\Service\Notifier\LazyQueues;
use Tobento\Service\Notifier\ChannelsInterface;
use Tobento\Service\Notifier\ChannelInterface;
use Tobento\Service\Notifier\ChannelFactoryInterface;
use Tobento\Service\Notifier\Symfony;
use Psr\Container\ContainerInterface;

$channels = new LazyChannels(
    container: $container, // ContainerInterface
    channels: [
        // using a factory:
        'sms' => [
            // factory must implement ChannelFactoryInterface
            'factory' => Symfony\ChannelFactory::class,
            'config' => [
                'dsn' => 'vonage://KEY:SECRET@default?from=FROM',
                'channel' => \Symfony\Component\Notifier\Channel\SmsChannel::class,
            ],
        ],

        // using a closure:
        'mail' => static function (string $name, ContainerInterface $c): ChannelInterface {
            // create channel ...
            return $channel;
        },

        // or you may sometimes just create the channel (not lazy):
        'sms/null' => new NullChannel(name: 'sms'),
    ],
);

var_dump($channels instanceof ChannelsInterface);
// bool(true)
```

Queue
-----

[](#queue)

You may queue your notification by just adding the `Queue::class` parameter:

```
use Tobento\Service\Notifier\Notification;
use Tobento\Service\Notifier\Parameter\Queue;

$notification = new Notification()
    ->parameter(new Queue(
        // you may specify the queue to be used:
        name: 'secondary',

        // you may specify a delay in seconds:
        delay: 30,

        // you may specify how many times to retry:
        retry: 3,

        // you may specify a priority:
        priority: 100,

        // you may specify if you want to encrypt the message:
        encrypt: true,
    ));
```

**Requirements**

To support queuing notifications you will need to pass a queue handler to the notifier.

Consider using the default queue handler using the [Queue Service](https://github.com/tobento-ch/service-queue):

**First, install the queue service:**

```
composer require tobento/service-queue
```

**Finally, pass the queue handler to the notifier:**

```
use Tobento\Service\Notifier\NotifierInterface;
use Tobento\Service\Notifier\Notifier;
use Tobento\Service\Notifier\ChannelsInterface;
use Tobento\Service\Notifier\Channels;
use Tobento\Service\Notifier\Queue\QueueHandler;
use Tobento\Service\Queue\QueueInterface;

$notifier = new Notifier(
    channels: new Channels(), // ChannelsInterface

    // set a queue handler:
    queueHandler: new QueueHandler(
        queue: $queue, // QueueInterface
        // you may define the default queue used if no specific is defined on the notification.
        queueName: 'mails', // null|string
    ),
);
```

Events
------

[](#events)

You may listen to the following events if your notifier is configured to support it.

EventDescription`Tobento\Service\Notifier\Event\NotificationSending::class`The Event will be fired **before** sending the notification.`Tobento\Service\Notifier\Event\NotificationSent::class`The Event is fired **after** the notification is sent.`Tobento\Service\Notifier\Event\NotificationQueued::class`The Event will be fired **after** queuing the notification.When using the [default notifier](#create-notifier) just pass an event dispatcher.

Credits
=======

[](#credits)

- [Tobias Strub](https://www.tobento.ch)
- [All Contributors](../../contributors)
- [Symfony](https://symfony.com)

###  Health Score

45

—

FairBetter than 91% of packages

Maintenance79

Regular maintenance activity

Popularity10

Limited adoption so far

Community12

Small or concentrated contributor base

Maturity67

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~76 days

Recently: every ~31 days

Total

12

Last Release

109d ago

Major Versions

1.x-dev → 2.02025-09-30

PHP version history (2 changes)1.0.0PHP &gt;=8.0

2.0PHP &gt;=8.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/055d6a1b5c2384bb179c75ab0b55914231d898fdc4dffeb30770f81200e52206?d=identicon)[TOBENTOch](/maintainers/TOBENTOch)

---

Top Contributors

[![tobento-ch](https://avatars.githubusercontent.com/u/16684832?v=4)](https://github.com/tobento-ch "tobento-ch (47 commits)")

---

Tags

phppackageemailnotifiersmstobento

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Type Coverage Yes

### Embed Badge

![Health badge](/badges/tobento-service-notifier/health.svg)

```
[![Health](https://phpackages.com/badges/tobento-service-notifier/health.svg)](https://phpackages.com/packages/tobento-service-notifier)
```

###  Alternatives

[symfony/symfony

The Symfony PHP framework

31.4k87.2M2.2k](/packages/symfony-symfony)[tempest/framework

The PHP framework that gets out of your way.

2.2k34.4k16](/packages/tempest-framework)[typo3/cms

TYPO3 CMS is a free open source Content Management Framework initially created by Kasper Skaarhoj and licensed under GNU/GPL.

1.2k1.9M122](/packages/typo3-cms)[moonshine/moonshine

Laravel administration panel

1.3k253.1k86](/packages/moonshine-moonshine)[ecotone/ecotone

Enterprise architecture layer for Laravel and Symfony — CQRS, Event Sourcing, Durable Workflows (Sagas, Orchestrators), Projections, and Outbox messaging via PHP attributes.

564576.7k54](/packages/ecotone-ecotone)[typo3/cms-core

TYPO3 CMS Core

3713.2M5.2k](/packages/typo3-cms-core)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
