Когда у меня есть один notifiable
пользователь, вставляется одна запись в таблицу notifications
вместе с mail
/ sms
которая отлично работает через каналы.
Проблема заключается в том, что когда у меня есть user
коллекция, список пользователей из 1k за мной, и я публикую обновление. Вот что происходит при использовании признака Notifiable
как предложено для многопользовательского случая:
mails
/ sms
отправлено (вопрос не здесь) notifications
БД Кажется, что добавление 1k уведомлений в таблицу notifications
БД не является оптимальным решением. Поскольку данные toArray
совпадают, а все остальное в таблице notifications
БД одинаково для строк 1k, с той лишь разницей, что notifiable_id
user
notifiable_type
.
Оптимальное решение из коробки:
array
notifiable_type
user_array
или user
с notifiable_id
0 (ноль будет использоваться только для обозначения его многозначного пользователя) Создайте / используйте другую таблицу notifications_read
используя notification_id
только что созданное как foreign_key
и вставьте строки 1k только из этих полей:
notification_id
read_at
Я надеюсь, что уже есть способ сделать это, поскольку я нахожусь в этом пункте в своем приложении, и хотел бы использовать встроенные уведомления и каналы для этой ситуации, поскольку я увольняю emails
/ sms
, что хорошо повторять В 1к раз я думаю, но это запись одних и тех же данных в базу данных, которая является проблемой, которая нуждается в оптимизации.
Любые мысли / идеи, как действовать в этой ситуации?
Быстрый пример:
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
.