php - real - Laravel 5.3-Notificación única para la colección de usuarios(seguidores)
real time notifications laravel (2)
Cuando tengo un único usuario notifiable
, se inserta una sola entrada en la tabla de notifications
, junto con un mail
/ sms
enviado que funciona perfectamente a través de los canales.
El problema es cuando tengo una colección de user
, una lista de usuarios de 1k que me siguen y publico una actualización. Esto es lo que sucede cuando se utiliza el rasgo Notifiable
como se sugiere para el caso de varios usuarios:
- 1k
mails
/sms
enviados (el problema no está aquí) - 1k entradas de notificación agregadas a la tabla de
notifications
la base de datos
Parece que agregar 1k notificaciones a la tabla de notificaciones de la base de datos no es una solución óptima. Dado que los datos de toArray
son los mismos, y todo lo demás en la tabla de notifications
la base de datos es el mismo para las filas de 1k, con la única diferencia es el notifiable_id
del user
notifiable_type
.
Una solución óptima fuera de la caja sería:
- Laravel reconocería el hecho de que se trata de una
array
notifiable_type
- Guarde una notificación única como
user_array
ouser
conuser_array
0 (cero solo se usaría para indicar que es un usuario con múltiples notificaciones) Cree / use otra tabla
notifications_read
usando lanotification_id
se acaba de crear comoforeign_key
e inserte filas de 1k, solo de estos campos:notification_id
read_at
Espero que ya haya una forma de hacerlo ya que estoy en este punto de mi aplicación y me encantaría usar los Notificaciones y canales integrados para esta situación, ya que estoy disparando las notificaciones de emails
/ sms
, lo cual está bien para repetir Creo que 1k veces, pero es la entrada de los mismos datos en la base de datos el problema que debe optimizarse.
¿Alguna idea / idea de cómo proceder en esta situación?
Actualizado 2017-01-14: implementado enfoque más correcto
Ejemplo rápido:
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();
}
});
¿Cómo hacer que funcione?
Paso 1 - migración - crear tabla (subnotificaciones)
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'');
}
}
Paso 2 - vamos a crear un modelo para la nueva tabla de subnotificaciones
<?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();
}
}
}
Paso 3 - crear un canal de notificación de base de datos personalizado
Actualizado : usando la variable estática $ map para mantener la primera identificación de notificación e insertar las siguientes notificaciones (con los mismos datos) sin crear un registro en la tabla de 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);
}
}
Paso 4 - crear una notificación
Actualizado : ahora la notificación es compatible con todos los canales, no solo con subnotificaciones
<?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;
}
}
Paso 5 - rasgo de ayudante para "seguidores"
<?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'');
}
}
Paso 6 - actualiza tu modelo de Usuarios
Actualizado : no requiere método de seguidores
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'',
];
}
Sí, tienes razón, supongo que con el rasgo Notifiable
predeterminado, puedes crear un canal personalizado .
Puede consultar la clase Illuminate/Notifications/Channels/DatabaseChannel
para la creación por defecto y adoptarla en una tabla dinámica.
Espero que esto ayude a crear un nuevo canal con una tabla dinámica. Además, implemente un rasgo HasDatabasePivotNotifications
(o nombre similar) a su propio rasgo Notifiable
.