Laravel 5.3 - 用户收集的单一通知(关注者)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/41274477/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Laravel 5.3 - Single Notification for User Collection (followers)
提问by Wonka
When I have a single notifiable
user, a single entry in the notifications
table is inserted, along with a mail
/sms
sent which is perfectly working via channels.
当我只有一个notifiable
用户时,notifications
会插入表中的一个条目,以及一个通过渠道完美工作的mail
/sms
发送。
The issue is when I have a user
collection, a list of 1k users following me, and I post an update. Here is what happens when using the Notifiable
trait as suggested for multi-user case:
问题是当我有一个user
收藏集,有 1k 个关注我的用户列表,然后我发布更新时。以下是Notifiable
按照多用户案例的建议使用trait时会发生的情况:
- 1k
mails
/sms
sent (issue is not here) - 1k notification entries added to the DB's
notifications
table
- 1k
mails
/sms
发送(问题不在这里) - 1k 通知条目添加到数据库的
notifications
表
It seems that adding 1k notifications to the DB's notifications
table is not an optimal solution. Since the toArray
data is the same, and everything else in the DB's notifications
table is the same for 1k rows, with the onlydifference being the notifiable_id
of the user
notifiable_type
.
似乎向数据库notifications
表中添加 1k 通知不是最佳解决方案。由于该toArray
数据是一样的,和一切在数据库中的notifications
表是1K行相同,与仅作为区别notifiable_id
的user
notifiable_type
。
An optimal solution out of the box would be:
开箱即用的最佳解决方案是:
- Laravel would pick up the fact that it's an
array
notifiable_type
- Save a singlenotification as
notifiable_type
user_array
oruser
withnotifiable_id
0 (zero would only be used to signify it's a multi notifiable user) Create/Use another table
notifications_read
using thenotification_id
it just created as theforeign_key
and insert 1k rows, of just these fields:notification_id
notifiable_id
notifiable_type
read_at
- Laravel 会发现它是一个
array
notifiable_type
- 将单个通知另存为0
notifiable_type
user_array
或user
使用notifiable_id
0(零仅用于表示它是多通知用户) 创建/使用另一台
notifications_read
使用notification_id
它刚创建的foreign_key
不仅仅是这些领域,并插入1K行:notification_id
notifiable_id
notifiable_type
read_at
I am hoping there is already a way to do this as I am at this point in my application and would love to use the built in Notifications and channels for this situation, as I am firing off emails
/sms
notifications, which is fine to repeat 1k times I think, but it's the entry of the same data into the database that is the problem that needs to be optimized.
我希望已经有一种方法可以做到这一点,因为我现在在我的应用程序中,并且很乐意在这种情况下使用内置的通知和渠道,因为我正在触发emails
/sms
通知,重复 1k 次就可以了我认为,但需要优化的问题是将相同的数据输入到数据库中。
Any thoughts/ideas how to proceed in this situation?
在这种情况下如何进行任何想法/想法?
采纳答案by Leonid Shumakov
Updated 2017-01-14: implemented more correct approach
2017-01-14 更新:实施了更正确的方法
Quick example:
快速示例:
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();
}
});
How to make it work?
如何使它工作?
Step 1- migration - create table (subnotifications)
第 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');
}
}
Step 2- let's create a model for new subnotifications table
第 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();
}
}
}
Step 3- create a custom database notification channel
Updated: using static variable $map to keep first notification id and insert next notifications (with the same data) without creating a record in notifications
table
第 3 步- 创建自定义数据库通知通道
更新:使用静态变量 $map 保留第一个通知 id 并插入下一个通知(具有相同数据)而不在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);
}
}
Step 4- create a notification
Updated: now notification supports all channels, not only subnotifications
第 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;
}
}
Step 5- helper trait for "followers"
第 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');
}
}
Step 6- update your Users model
Updated: no required followersmethod
第 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',
];
}
回答by Isfettcom
Yes you are right i guess with the default Notifiable
trait, you could create a custom channel.
是的,我猜您是对的,使用默认Notifiable
特征,您可以创建自定义频道。
You can check the Illuminate\Notifications\Channels\DatabaseChannel
class for default creation and adopt it to a pivot-table one.
您可以检查Illuminate\Notifications\Channels\DatabaseChannel
默认创建的类并将其用于数据透视表。
Hope this helps to create a new channel with a pivot table. Also, implement a HasDatabasePivotNotifications
trait (or similar name) to your own Notifiable
trait.
希望这有助于创建一个带有数据透视表的新频道。此外,HasDatabasePivotNotifications
为您自己的Notifiable
特征实现特征(或类似名称)。