diff --git a/README.md b/README.md
index db3b4b4..5975072 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ Laravel Snooze
[![License](https://poser.pugx.org/thomasjohnkane/snooze/license)](https://packagist.org/packages/thomasjohnkane/snooze)
### Why use this package?
-- Ever wanted to schedule a future notification to go out at a specific time? (was the delayed queue option not enough?)
+- Ever wanted to schedule a future notification to go out at a specific time? (was the delayed queue option not enough?)
- Want a simple on-boarding email drip?
- How about happy birthday emails?
@@ -44,7 +44,7 @@ php artisan vendor:publish --provider="Thomasjohnkane\Snooze\ServiceProvider" --
## Usage
#### Using the model trait
-Snooze provides a trait for your model, similar to the standard `Notifiable` trait.
+Snooze provides a trait for your model, similar to the standard `Notifiable` trait.
It adds a `notifyAt()` method to your model to schedule notifications.
```php
@@ -68,7 +68,7 @@ $user->notifyAt(new NewYearNotification, Carbon::parse('last day of this year'))
```
#### Using the ScheduledNotification::create helper
-You can also use the `create` method on the `ScheduledNotification`.
+You can also use the `create` method on the `ScheduledNotification`.
```php
ScheduledNotification::create(
Auth::user(), // Target
@@ -92,7 +92,7 @@ ScheduledNotification::create(
#### An important note about scheduling the `snooze:send` command
-Creating a scheduled notification will add the notification to the database. It will be sent by running `snooze:send` command at (or after) the stored `sendAt` time.
+Creating a scheduled notification will add the notification to the database. It will be sent by running `snooze:send` command at (or after) the stored `sendAt` time.
The `snooze:send` command is scheduled to run every minute by default. You can change this value (`sendFrequency`) in the published config file. Available options are `everyMinute`, `everyFiveMinutes`, `everyTenMinutes`, `everyFifteenMinutes`, `everyThirtyMinutes`, `hourly`, and `daily`.
@@ -100,15 +100,15 @@ The only thing you need to do is make sure `schedule:run` is also running. You c
### Setting the send tolerance
-If your scheduler stops working, a backlog of scheduled notifications will build up. To prevent users receiving all of
-the old scheduled notifications at once, the command will only send mail within the configured tolerance.
+If your scheduler stops working, a backlog of scheduled notifications will build up. To prevent users receiving all of
+the old scheduled notifications at once, the command will only send mail within the configured tolerance.
By default this is set to 24 hours, so only mail scheduled to be sent within that window will be sent. This can be
-configured (in seconds) using the `SCHEDULED_NOTIFICATION_SEND_TOLERANCE` environment variable or in the `snooze.php` config file.
+configured (in seconds) using the `SCHEDULED_NOTIFICATION_SEND_TOLERANCE` environment variable or in the `snooze.php` config file.
### Setting the prune age
The package can prune sent and cancelled messages that were sent/cancelled more than x days ago. You can
-configure this using the `SCHEDULED_NOTIFICATION_PRUNE_AGE` environment variable or in the `snooze.php` config file
+configure this using the `SCHEDULED_NOTIFICATION_PRUNE_AGE` environment variable or in the `snooze.php` config file
(unit is days). This feature is turned off by default.
#### Detailed Examples
@@ -165,6 +165,53 @@ public function shouldInterrupt($notifiable) {
If this method is not present on your notification, the notification will *not* be interrupted. Consider creating a shouldInterupt trait if you'd like to repeat conditional logic on groups of notifications.
+**Scheduled Notification Meta Information**
+
+It's possible to store meta information on a scheduled notification, and then query the scheduled notifications by this meta information at a later stage.
+
+This functionality could be useful for when you store notifications for a future date, but some change in the system requires
+you to update them. By using the meta column, it's possible to more easily query these scheduled notifications from the database by something else than
+the notifiable.
+
+***Storing Meta Information***
+
+Using the `ScheduledNotification::create` helper
+
+```php
+ScheduledNotification::create(
+ $target, // Target
+ new ScheduledNotificationExample($order), // Notification
+ Carbon::now()->addDay(), // Send At,
+ ['foo' => 'bar'] // Meta Information
+);
+```
+
+Using the `notifyAt` trait
+
+```php
+ $user->notifyAt(new BirthdayNotification, Carbon::parse($user->birthday), ['foo' => 'bar']);
+```
+
+***Retrieving Meta Information from Scheduled Notifications***
+
+You can call the `getMeta` function on an existing scheduled notification to retrieve the meta information for the specific notification.
+
+Passing no parameters to this function will return the entire meta column in array form.
+
+Passing a string key (`getMeta('foo')`), will retrieve the specific key from the meta column.
+
+***Querying Scheduled Notifications using the ScheduledNotification::findByMeta helper***
+
+It's possible to query the database for scheduled notifications with certain meta information, by using the `findByMeta` helper.
+
+```php
+ ScheduledNotification::findByMeta('foo', 'bar'); //key and value
+```
+
+The first parameter is the meta key, and the second parameter is the value to look for.
+
+>Note: The index column doesn't currently make use of a database index
+
**Conditionally turn off scheduler**
If you would like to disable sending of scheduled notifications, set an env variable of `SCHEDULED_NOTIFICATIONS_DISABLED` to `true`. You will still be able to schedule notifications, and they will be sent once the scheduler is enabled.
@@ -191,6 +238,9 @@ composer test
If you discover any security related issues, please email instead of using the issue tracker.
+## Changelog
+Please be sure to run `php artisan migrate` when upgrading versions of this package.
+
## Contributing
1. Fork it ()
diff --git a/migrations/2021_09_10_130000_add_meta_to_scheduled_notifications.php b/migrations/2021_09_10_130000_add_meta_to_scheduled_notifications.php
new file mode 100644
index 0000000..70ef271
--- /dev/null
+++ b/migrations/2021_09_10_130000_add_meta_to_scheduled_notifications.php
@@ -0,0 +1,32 @@
+json('meta')->nullable();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table(config('snooze.table'), function (Blueprint $table) {
+ $table->dropColumn('meta');
+ });
+ }
+}
diff --git a/src/Models/ScheduledNotification.php b/src/Models/ScheduledNotification.php
index 92e7871..5c58246 100644
--- a/src/Models/ScheduledNotification.php
+++ b/src/Models/ScheduledNotification.php
@@ -36,6 +36,7 @@ class ScheduledNotification extends Model
'cancelled',
'created_at',
'updated_at',
+ 'meta',
];
protected $attributes = [
@@ -44,6 +45,10 @@ class ScheduledNotification extends Model
'cancelled_at' => null,
];
+ protected $casts = [
+ 'meta' => 'array',
+ ];
+
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
diff --git a/src/ScheduledNotification.php b/src/ScheduledNotification.php
index e8a36a7..35704a8 100644
--- a/src/ScheduledNotification.php
+++ b/src/ScheduledNotification.php
@@ -30,6 +30,7 @@ public function __construct(ScheduledNotificationModel $scheduleNotificationMode
* @param object $notifiable
* @param Notification $notification
* @param DateTimeInterface $sendAt
+ * @param array $meta
* @return self
*
* @throws SchedulingFailedException
@@ -37,10 +38,12 @@ public function __construct(ScheduledNotificationModel $scheduleNotificationMode
public static function create(
object $notifiable,
Notification $notification,
- DateTimeInterface $sendAt
+ DateTimeInterface $sendAt,
+ array $meta = []
): self {
if ($sendAt <= Carbon::now()->subMinute()) {
- throw new SchedulingFailedException(sprintf('`send_at` must not be in the past: %s', $sendAt->format(DATE_ISO8601)));
+ throw new SchedulingFailedException(sprintf('`send_at` must not be in the past: %s',
+ $sendAt->format(DATE_ISO8601)));
}
if (! method_exists($notifiable, 'notify')) {
@@ -54,12 +57,13 @@ public static function create(
$targetType = $notifiable instanceof AnonymousNotifiable ? AnonymousNotifiable::class : get_class($notifiable);
return new self($modelClass::create([
- 'target_id' => $targetId,
- 'target_type' => $targetType,
+ 'target_id' => $targetId,
+ 'target_type' => $targetType,
'notification_type' => get_class($notification),
- 'target' => $serializer->serialize($notifiable),
- 'notification' => $serializer->serialize($notification),
- 'send_at' => $sendAt,
+ 'target' => $serializer->serialize($notifiable),
+ 'notification' => $serializer->serialize($notification),
+ 'send_at' => $sendAt,
+ 'meta' => $meta,
]));
}
@@ -99,6 +103,17 @@ public static function findByTarget(object $notifiable): ?Collection
return self::collection($models);
}
+ public static function findByMeta($key, $value): ?Collection
+ {
+ $modelClass = self::getScheduledNotificationModelClass();
+
+ $models = $modelClass::query()
+ ->where("meta->{$key}", $value)
+ ->get();
+
+ return self::collection($models);
+ }
+
public static function all(bool $includeSent = false, bool $includeCanceled = false): Collection
{
$modelClass = self::getScheduledNotificationModelClass();
@@ -143,7 +158,7 @@ public static function cancelAnonymousNotificationsByChannel(string $channel, st
->get()
->map(function (ScheduledNotificationModel $model) use ($serializer) {
return [
- 'id' => $model->id,
+ 'id' => $model->id,
'routes' => $serializer->unserialize($model->target)->routes,
];
})
@@ -277,6 +292,18 @@ public function getUpdatedAt(): CarbonInterface
return $this->scheduleNotificationModel->updated_at;
}
+ /**
+ * @param null $key
+ */
+ public function getMeta($key = null)
+ {
+ if (is_null($key)) {
+ return $this->scheduleNotificationModel->meta;
+ } else {
+ return $this->scheduleNotificationModel->meta[$key] ?? [];
+ }
+ }
+
/**
* @return bool
*/
diff --git a/src/Traits/SnoozeNotifiable.php b/src/Traits/SnoozeNotifiable.php
index 73c226d..2888679 100644
--- a/src/Traits/SnoozeNotifiable.php
+++ b/src/Traits/SnoozeNotifiable.php
@@ -14,12 +14,13 @@ trait SnoozeNotifiable
/**
* @param Notification $notification
* @param DateTimeInterface $sendAt
+ * @param array $meta
* @return ScheduledNotification
*
* @throws SchedulingFailedException
*/
- public function notifyAt($notification, DateTimeInterface $sendAt): ScheduledNotification
+ public function notifyAt($notification, DateTimeInterface $sendAt, array $meta = []): ScheduledNotification
{
- return ScheduledNotification::create($this, $notification, $sendAt);
+ return ScheduledNotification::create($this, $notification, $sendAt, $meta);
}
}
diff --git a/tests/ScheduledNotificationTest.php b/tests/ScheduledNotificationTest.php
index 3936eca..d41159d 100644
--- a/tests/ScheduledNotificationTest.php
+++ b/tests/ScheduledNotificationTest.php
@@ -35,6 +35,7 @@ public function testItRunsMigrations()
'cancelled_at',
'created_at',
'updated_at',
+ 'meta',
], $columns);
}
@@ -247,7 +248,8 @@ public function testNotificationsCanBeQueried()
ScheduledNotification::create(
$target,
new TestNotification(User::find(2)),
- Carbon::now()->addSeconds(10)
+ Carbon::now()->addSeconds(10),
+ ['foo' => 'baz']
);
ScheduledNotification::create(
@@ -259,7 +261,8 @@ public function testNotificationsCanBeQueried()
ScheduledNotification::create(
$target,
new TestNotification(User::find(2)),
- Carbon::now()->addSeconds(60)
+ Carbon::now()->addSeconds(60),
+ ['foo' => 'bar']
);
ScheduledNotification::create(
@@ -271,7 +274,8 @@ public function testNotificationsCanBeQueried()
ScheduledNotification::create(
$target,
new TestNotificationTwo(User::find(2)),
- Carbon::now()->addSeconds(60)
+ Carbon::now()->addSeconds(60),
+ ['foo' => 'bar']
);
$all = ScheduledNotification::all();
@@ -285,6 +289,9 @@ public function testNotificationsCanBeQueried()
$this->assertSame(5, ScheduledNotification::findByTarget($target)->count());
+ $this->assertSame(2, ScheduledNotification::findByMeta('foo', 'bar')->count());
+ $this->assertSame(1, ScheduledNotification::findByMeta('foo', 'baz')->count());
+
$all->first()->sendNow();
$allNotSent = ScheduledNotification::all();
@@ -304,4 +311,22 @@ public function testNotificationClassCanBeRetreived()
$this->assertInstanceOf(TestNotification::class, $scheduled_notification->getNotification());
$this->assertEquals($scheduled_notification->getNotification()->newUser->email, $notification->newUser->email);
}
+
+ public function testItCanStoreAndRetrieveMetaInfo()
+ {
+ $target = User::find(1);
+ $notification = new TestNotification(User::find(2));
+
+ $meta = ['foo' => 'bar', 'hey' => 'you'];
+
+ $scheduled_notification = ScheduledNotification::create($target, $notification, Carbon::now()->addSeconds(10), $meta);
+ $scheduled_notification_no_meta = ScheduledNotification::create($target, $notification, Carbon::now()->addSeconds(10));
+
+ $this->assertSame($meta, $scheduled_notification->getMeta());
+ $this->assertSame([], $scheduled_notification_no_meta->getMeta());
+
+ $this->assertSame('bar', $scheduled_notification->getMeta('foo'));
+ $this->assertSame('you', $scheduled_notification->getMeta('hey'));
+ $this->assertSame([], $scheduled_notification->getMeta('doesnt_exist'));
+ }
}