2016-05-27 21:12:40 +02:00
|
|
|
Developing notifications for Pony.fm
|
|
|
|
====================================
|
|
|
|
|
|
|
|
Pony.fm's notification system is designed around "drivers" for various
|
|
|
|
notification delivery methods. The types of notification one can receive
|
|
|
|
are defined in the
|
|
|
|
[`NotificationHandler`](app/Contracts/NotificationHandler.php)
|
|
|
|
interface, which is implemented by every class that needs to know about
|
|
|
|
the various notification types.
|
|
|
|
|
|
|
|
|
|
|
|
Sending a notification
|
|
|
|
----------------------
|
|
|
|
|
|
|
|
The `Notification` facade is used to send notifications as follows:
|
|
|
|
|
|
|
|
```php
|
|
|
|
use Notification;
|
|
|
|
|
|
|
|
// Something happens, like a newtrack getting published.
|
|
|
|
$track = new Track();
|
|
|
|
...
|
|
|
|
|
|
|
|
// The "something" is done happening! Time to send a notification.
|
|
|
|
Notification::publishedTrack($track);
|
|
|
|
```
|
|
|
|
|
|
|
|
This facade has a method for every notification type, drawn from the
|
|
|
|
[`NotificationHandler`](../app/Contracts/NotificationHandler.php) interface.
|
|
|
|
Each of these methods accepts the data needed to build a notification
|
|
|
|
message and a list of the notification's recipients.
|
|
|
|
|
|
|
|
|
|
|
|
Adding new notification types
|
|
|
|
-----------------------------
|
|
|
|
|
|
|
|
1. Add a method for the new notification type to the
|
|
|
|
[`NotificationHandler`](../app/Contracts/NotificationHandler.php)
|
|
|
|
interface.
|
|
|
|
|
|
|
|
2. Implement the new methods in every class that implements the
|
|
|
|
interface. Use your IDE to find these. An inexhaustive list:
|
|
|
|
|
|
|
|
- [`NotificationManager`](../app/Library/Notifications/NotificationManager.php)
|
|
|
|
- [`RecipientFinder`](../app/Library/Notifications/RecipientFinder.php)
|
|
|
|
- [`PonyfmDriver`](../app/Library/Notifications/PonyfmDriver.php)
|
2016-12-09 11:53:32 +01:00
|
|
|
|
|
|
|
3. Ensure you create HTML and plaintext templates for the email version of the
|
|
|
|
notification.
|
2016-05-27 21:12:40 +02:00
|
|
|
|
2016-12-09 11:53:32 +01:00
|
|
|
4. Call the new method on the `Notification` facade from wherever the
|
2016-05-27 21:12:40 +02:00
|
|
|
new notification gets triggered.
|
|
|
|
|
2016-12-09 11:53:32 +01:00
|
|
|
5. Implement any necessary logic for the new notification type in the
|
2016-05-27 21:12:40 +02:00
|
|
|
[`Activity`](../app/Models/Activity.php) model.
|
|
|
|
|
|
|
|
|
|
|
|
Adding new notification drivers
|
|
|
|
-------------------------------
|
|
|
|
|
|
|
|
1. Create a new class for the driver that implements the
|
|
|
|
[`NotificationHandler`](../app/Contracts/NotificationHandler.php)
|
|
|
|
interface.
|
|
|
|
|
|
|
|
2. Make each method from the above interface send the corresponding type
|
|
|
|
of notification to everyone who is to receive it via that driver.
|
|
|
|
Implement UI and API integrations as needed.
|
|
|
|
|
|
|
|
3. Modify the
|
|
|
|
[`RecipientFinder`](../app/Library/Notifications/RecipientFinder.php)
|
|
|
|
class to build recipient lists for the new driver.
|
|
|
|
|
|
|
|
|
|
|
|
Architectural notes
|
|
|
|
-------------------
|
|
|
|
|
|
|
|
The notification system is designed around two ideas: being as type-safe
|
|
|
|
as PHP allows it to be, and doing all the processing and sending of
|
|
|
|
notifications asynchronously.
|
|
|
|
|
|
|
|
To that end, the
|
|
|
|
[`NotificationManager`](../app/Library/Notifications/NotificationManager.php)
|
|
|
|
class is a thin wrapper around the `SendNotifications` job. The job
|
|
|
|
calls the notification drivers asynchronously to actually send the
|
|
|
|
notifications. This job should run on a dedicated queue in production.
|
|
|
|
|
|
|
|
The [`NotificationHandler`](../app/Contracts/NotificationHandler.php)
|
|
|
|
interface is key to maintaining type safety - it ensures that drivers
|
|
|
|
and `NotificationManager` all support every type of notification. All
|
|
|
|
classes that have logic specific to a notification type implement this
|
|
|
|
interface to ensure that all notification types are handled.
|
|
|
|
|
|
|
|
There's one exception to the use of `NotificationHandler` - the
|
|
|
|
[`Activity`](../app/Models/Activity.php) model. The logic for mapping the
|
|
|
|
data we store about an activity in the database to a notification's API
|
|
|
|
representation had to go somewhere, and using the `NotificationHandler`
|
|
|
|
interface here would have made this logic a lot more obtuse.
|
2016-12-09 11:53:32 +01:00
|
|
|
|
|
|
|
### Data flow
|
|
|
|
|
|
|
|
1. Some action that triggers a notification calls the `NotificationManager`
|
|
|
|
facade.
|
|
|
|
|
|
|
|
2. An asynchronous job is kicked off that figures out how to send the
|
|
|
|
notification.
|
|
|
|
|
|
|
|
3. An `Activity` record is created for the action.
|
|
|
|
|
|
|
|
4. A `Notification` record is created for every user who is to receive a
|
|
|
|
notification about that activity. These records act as Pony.fm's on-site
|
|
|
|
notifications and cannot be disabled.
|
|
|
|
|
|
|
|
5. Depending on subscription preferences, push and email notifications will be
|
|
|
|
sent out as well, each creating their own respective database records. These
|
|
|
|
are linked to a `Notification` record for unified read/unread tracking.
|
|
|
|
|
|
|
|
6. A `Notification` record is marked read when it is viewed on-site or any other
|
|
|
|
notification type associated with it (like an email or push notification) is
|
|
|
|
clicked.
|