Laravel 5.3 – Одиночное уведомление для пользовательской коллекции (последователи)

Когда у меня есть один notifiable пользователь, вставляется одна запись в таблицу notifications вместе с mail / sms которая отлично работает через каналы.

Проблема заключается в том, что когда у меня есть user коллекция, список пользователей из 1k за мной, и я публикую обновление. Вот что происходит при использовании признака Notifiable как предложено для многопользовательского случая:

  1. 1k mails / sms отправлено (вопрос не здесь)
  2. 1k записей уведомлений, добавленных в таблицу notifications БД

Кажется, что добавление 1k уведомлений в таблицу notifications БД не является оптимальным решением. Поскольку данные toArray совпадают, а все остальное в таблице notifications БД одинаково для строк 1k, с той лишь разницей, что notifiable_id user notifiable_type .

Оптимальное решение из коробки:

  1. Laravel подобрал бы тот факт, что это array notifiable_type
  2. Сохраните одно уведомление как user_array или user с notifiable_id 0 (ноль будет использоваться только для обозначения его многозначного пользователя)
  3. Создайте / используйте другую таблицу notifications_read используя notification_id только что созданное как foreign_key и вставьте строки 1k только из этих полей:

    notification_id read_at

Я надеюсь, что уже есть способ сделать это, поскольку я нахожусь в этом пункте в своем приложении, и хотел бы использовать встроенные уведомления и каналы для этой ситуации, поскольку я увольняю emails / sms , что хорошо повторять В 1к раз я думаю, но это запись одних и тех же данных в базу данных, которая является проблемой, которая нуждается в оптимизации.

Любые мысли / идеи, как действовать в этой ситуации?

Обновлено 2017-01-14: реализован более правильный подход

Быстрый пример:

 use Illuminate\Support\Facades\Notification; use App\Notifications\SomethingCoolHappen; Route::get('/step1', function () { // example - my followers $followers = App\User::all(); // notify them Notification::send($followers, new SomethingCoolHappen(['arg1' => 1, 'arg2' => 2])); }); Route::get('/step2', function () { // my follower $user = App\User::find(10); // check unread subnotifications foreach ($user->unreadSubnotifications as $subnotification) { var_dump($subnotification->notification->data); $subnotification->markAsRead(); } }); 

Как заставить его работать?

Шаг 1 – миграция – создание таблицы (субнотификации)

 use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateSubnotificationsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('subnotifications', function (Blueprint $table) { // primary key $table->increments('id')->primary(); // notifications.id $table->uuid('notification_id'); // notifiable_id and notifiable_type $table->morphs('notifiable'); // follower - read_at $table->timestamp('read_at')->nullable(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('subnotifications'); } } 

Шаг 2 – давайте создадим модель для новой таблицы субнотификаций

 <?php // App\Notifications\Subnotification.php namespace App\Notifications; use Illuminate\Database\Eloquent\Model; use Illuminate\Notifications\DatabaseNotification; use Illuminate\Notifications\DatabaseNotificationCollection; class Subnotification extends Model { // we don't use created_at/updated_at public $timestamps = false; // nothing guarded - mass assigment allowed protected $guarded = []; // cast read_at as datetime protected $casts = [ 'read_at' => 'datetime', ]; // set up relation to the parent notification public function notification() { return $this->belongsTo(DatabaseNotification::class); } /** * Get the notifiable entity that the notification belongs to. */ public function notifiable() { return $this->morphTo(); } /** * Mark the subnotification as read. * * @return void */ public function markAsRead() { if (is_null($this->read_at)) { $this->forceFill(['read_at' => $this->freshTimestamp()])->save(); } } } 

Шаг 3 – создать канал уведомлений пользовательской базы данных
Обновлено : использование статической переменной $ map для хранения первого идентификатора уведомления и вставки следующих уведомлений (с теми же данными) без создания записи в таблице notifications

 <?php // App\Channels\SubnotificationsChannel.php namespace App\Channels; use Illuminate\Notifications\DatabaseNotification; use Illuminate\Notifications\Notification; class SubnotificationsChannel { /** * Send the given notification. * * @param mixed $notifiable * @param \Illuminate\Notifications\Notification $notification * * @return void */ public function send($notifiable, Notification $notification) { static $map = []; $notificationId = $notification->id; // get notification data $data = $this->getData($notifiable, $notification); // calculate hash $hash = md5(json_encode($data)); // if hash is not in map - create parent notification record if (!isset($map[$hash])) { // create original notification record with empty notifiable_id DatabaseNotification::create([ 'id' => $notificationId, 'type' => get_class($notification), 'notifiable_id' => 0, 'notifiable_type' => get_class($notifiable), 'data' => $data, 'read_at' => null, ]); $map[$hash] = $notificationId; } else { // otherwise use another/first notification id $notificationId = $map[$hash]; } // create subnotification $notifiable->subnotifications()->create([ 'notification_id' => $notificationId, 'read_at' => null ]); } /** * Prepares data * * @param mixed $notifiable * @param \Illuminate\Notifications\Notification $notification * * @return mixed */ public function getData($notifiable, Notification $notification) { return $notification->toArray($notifiable); } } 

Шаг 4 – создайте уведомление
Обновлено : теперь уведомление поддерживает все каналы, а не только субнотики

 <?php // App\Notifications\SomethingCoolHappen.php namespace App\Notifications; use App\Channels\SubnotificationsChannel; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Notification; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; class SomethingCoolHappen extends Notification { use Queueable; protected $data; /** * Create a new notification instance. * * @return void */ public function __construct($data) { $this->data = $data; } /** * Get the notification's delivery channels. * * @param mixed $notifiable * @return array */ public function via($notifiable) { /** * THIS IS A GOOD PLACE FOR DETERMINING NECESSARY CHANNELS */ $via = []; $via[] = SubnotificationsChannel::class; //$via[] = 'mail'; return $via; } /** * Get the mail representation of the notification. * * @param mixed $notifiable * @return \Illuminate\Notifications\Messages\MailMessage */ public function toMail($notifiable) { return (new MailMessage) ->line('The introduction to the notification.') ->action('Notification Action', 'https://laravel.com') ->line('Thank you for using our application!'); } /** * Get the array representation of the notification. * * @param mixed $notifiable * @return array */ public function toArray($notifiable) { return $this->data; } } 

Шаг 5 – вспомогательная черта для «последователей»

 <?php // App\Notifications\HasSubnotifications.php namespace App\Notifications; trait HasSubnotifications { /** * Get the entity's notifications. */ public function Subnotifications() { return $this->morphMany(Subnotification::class, 'notifiable') ->orderBy('id', 'desc'); } /** * Get the entity's read notifications. */ public function readSubnotifications() { return $this->Subnotifications() ->whereNotNull('read_at'); } /** * Get the entity's unread notifications. */ public function unreadSubnotifications() { return $this->Subnotifications() ->whereNull('read_at'); } } 

Шаг 6 – обновите модель своих пользователей
Обновлено : не требуется метод последовательных подписчиков

 namespace App; use App\Notifications\HasSubnotifications; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use Notifiable; /** * Adding helpers to followers: * * $user->subnotifications - all subnotifications * $user->unreadSubnotifications - all unread subnotifications * $user->readSubnotifications - all read subnotifications */ use HasSubnotifications; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'password', ]; /** * The attributes that should be hidden for arrays. * * @var array */ protected $hidden = [ 'password', 'remember_token', ]; } 

Да, вы правы, я думаю, с признаком Notifiable по умолчанию, вы можете создать собственный канал .

Вы можете проверить класс Illuminate\Notifications\Channels\DatabaseChannel для создания по умолчанию и перенести его в сводную таблицу.

Надеемся, что это поможет создать новый канал со сводной таблицей. Кроме того, HasDatabasePivotNotifications свойство HasDatabasePivotNotifications (или аналогичное имя) для вашего собственного Notifiable .