Notifications into the queue

Forces notifications into a dedicated SendNotificationsJob and passed
to the queue.

- One static method re-used in the job ::getAttributes, is that okay or
  use a trait?
- Do we want to use this solution and refactor into a better Hub after
  stable, postpone this implementation or use it in b11?
This commit is contained in:
Daniël Klabbers 2019-11-13 11:58:52 +01:00 committed by Franz Liedke
parent 20bc1e50fc
commit 8cdcb25fcc
7 changed files with 174 additions and 79 deletions

View File

@ -0,0 +1,23 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Notification\Blueprint;
abstract class AbstractBlueprint implements BlueprintInterface
{
public function getAttributes(): array
{
return [
'type' => static::getType(),
'from_user_id' => ($fromUser = $this->getFromUser()) ? $fromUser->id : null,
'subject_id' => ($subject = $this->getSubject()) ? $subject->getKey() : null,
'data' => ($data = $this->getData()) ? json_encode($data) : null
];
}
}

View File

@ -9,6 +9,9 @@
namespace Flarum\Notification\Blueprint;
use Flarum\Database\AbstractModel;
use Flarum\User\User;
/**
* A notification BlueprintInterface, when instantiated, represents a notification about
* something. The blueprint is used by the NotificationSyncer to commit the
@ -19,14 +22,14 @@ interface BlueprintInterface
/**
* Get the user that sent the notification.
*
* @return \Flarum\User\User|null
* @return User|null
*/
public function getFromUser();
/**
* Get the model that is the subject of this activity.
*
* @return \Flarum\Database\AbstractModel|null
* @return AbstractModel|null
*/
public function getSubject();

View File

@ -0,0 +1,39 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Notification\Event;
use Flarum\Notification\Blueprint\BlueprintInterface;
class Notifying
{
/**
* The blueprint for the notification.
*
* @var BlueprintInterface
*/
public $blueprint;
/**
* The users that the notification will be sent to.
*
* @var array|int[]
*/
public $users;
/**
* @param \Flarum\Notification\Blueprint\BlueprintInterface $blueprint
* @param \Flarum\User\User[] $users
*/
public function __construct(BlueprintInterface $blueprint, array &$users)
{
$this->blueprint = $blueprint;
$this->users = &$users;
}
}

View File

@ -23,7 +23,7 @@ class Sending
/**
* The users that the notification will be sent to.
*
* @var array
* @var array|int[]
*/
public $users;

View File

@ -0,0 +1,73 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Notification\Job;
use Carbon\Carbon;
use Flarum\Notification\Blueprint\BlueprintInterface;
use Flarum\Notification\Event\Notifying;
use Flarum\Notification\Event\Sending;
use Flarum\Notification\MailableInterface;
use Flarum\Notification\Notification;
use Flarum\Notification\NotificationMailer;
use Flarum\Queue\AbstractJob;
use Flarum\User\User;
class SendNotificationsJob extends AbstractJob
{
/**
* @var BlueprintInterface
*/
private $blueprint;
/**
* @var array
*/
private $recipientIds;
public function __construct(BlueprintInterface $blueprint, array $recipientIds = [])
{
$this->blueprint = $blueprint;
$this->recipientIds = $recipientIds;
}
public function handle(NotificationMailer $mailer)
{
$now = Carbon::now('utc')->toDateTimeString();
$recipients = $this->recipientIds;
event(new Sending($this->blueprint, $recipients));
$attributes = $this->blueprint->getAttributes();
Notification::insert(
array_map(function (User $user) use ($attributes, $now) {
return $attributes + [
'user_id' => $user->id,
'created_at' => $now
];
}, $recipients)
);
event(new Notifying($this->blueprint, $recipients));
if ($this->blueprint instanceof MailableInterface) {
$this->email($mailer, $this->blueprint, $recipients);
}
}
protected function email(NotificationMailer $mailer, MailableInterface $blueprint, array $recipients)
{
foreach ($recipients as $user) {
if ($user->shouldEmail($blueprint::getType())) {
$mailer->send($blueprint, $user);
}
}
}
}

View File

@ -9,10 +9,10 @@
namespace Flarum\Notification;
use Carbon\Carbon;
use Flarum\Notification\Blueprint\BlueprintInterface;
use Flarum\Notification\Event\Sending;
use Flarum\Notification\Job\SendNotificationsJob;
use Flarum\User\User;
use Illuminate\Contracts\Queue\Queue;
/**
* The Notification Syncer commits notification blueprints to the database, and
@ -42,20 +42,16 @@ class NotificationSyncer
protected $notifications;
/**
* @var NotificationMailer
* @var Queue
*/
protected $mailer;
protected $queue;
/**
* @param NotificationRepository $notifications
* @param NotificationMailer $mailer
*/
public function __construct(
NotificationRepository $notifications,
NotificationMailer $mailer
Queue $queue
) {
$this->notifications = $notifications;
$this->mailer = $mailer;
$this->queue = $queue;
}
/**
@ -69,7 +65,7 @@ class NotificationSyncer
*/
public function sync(Blueprint\BlueprintInterface $blueprint, array $users)
{
$attributes = $this->getAttributes($blueprint);
$attributes = $blueprint->getAttributes();
// Find all existing notification records in the database matching this
// blueprint. We will begin by assuming that they all need to be
@ -87,7 +83,7 @@ class NotificationSyncer
continue;
}
$existing = $toDelete->first(function ($notification, $i) use ($user) {
$existing = $toDelete->first(function ($notification) use ($user) {
return $notification->user_id === $user->id;
});
@ -115,7 +111,7 @@ class NotificationSyncer
// receiving this notification for the first time (we know because they
// didn't have a record in the database).
if (count($newRecipients)) {
$this->sendNotifications($blueprint, $newRecipients);
$this->queue->push(new SendNotificationsJob($blueprint, $newRecipients));
}
}
@ -127,7 +123,7 @@ class NotificationSyncer
*/
public function delete(BlueprintInterface $blueprint)
{
Notification::where($this->getAttributes($blueprint))->update(['is_deleted' => true]);
Notification::where($blueprint->getAttributes())->update(['is_deleted' => true]);
}
/**
@ -138,7 +134,7 @@ class NotificationSyncer
*/
public function restore(BlueprintInterface $blueprint)
{
Notification::where($this->getAttributes($blueprint))->update(['is_deleted' => false]);
Notification::where($blueprint->getAttributes())->update(['is_deleted' => false]);
}
/**
@ -158,50 +154,6 @@ class NotificationSyncer
static::$onePerUser = false;
}
/**
* Create a notification record and send an email (depending on user
* preference) from a blueprint to a list of recipients.
*
* @param \Flarum\Notification\Blueprint\BlueprintInterface $blueprint
* @param User[] $recipients
*/
protected function sendNotifications(Blueprint\BlueprintInterface $blueprint, array $recipients)
{
$now = Carbon::now('utc')->toDateTimeString();
event(new Sending($blueprint, $recipients));
$attributes = $this->getAttributes($blueprint);
Notification::insert(
array_map(function (User $user) use ($attributes, $now) {
return $attributes + [
'user_id' => $user->id,
'created_at' => $now
];
}, $recipients)
);
if ($blueprint instanceof MailableInterface) {
$this->mailNotifications($blueprint, $recipients);
}
}
/**
* Mail a notification to a list of users.
*
* @param MailableInterface $blueprint
* @param User[] $recipients
*/
protected function mailNotifications(MailableInterface $blueprint, array $recipients)
{
foreach ($recipients as $user) {
if ($user->shouldEmail($blueprint::getType())) {
$this->mailer->send($blueprint, $user);
}
}
}
/**
* Set the deleted status of a list of notification records.
*
@ -212,21 +164,4 @@ class NotificationSyncer
{
Notification::whereIn('id', $ids)->update(['is_deleted' => $isDeleted]);
}
/**
* Construct an array of attributes to be stored in a notification record in
* the database, given a notification blueprint.
*
* @param \Flarum\Notification\Blueprint\BlueprintInterface $blueprint
* @return array
*/
protected function getAttributes(Blueprint\BlueprintInterface $blueprint)
{
return [
'type' => $blueprint::getType(),
'from_user_id' => ($fromUser = $blueprint->getFromUser()) ? $fromUser->id : null,
'subject_id' => ($subject = $blueprint->getSubject()) ? $subject->id : null,
'data' => ($data = $blueprint->getData()) ? json_encode($data) : null
];
}
}

View File

@ -0,0 +1,22 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Queue;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class AbstractJob implements ShouldQueue
{
use InteractsWithQueue;
use Queueable;
use SerializesModels;
}