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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 14:58:28  来源:igfitidea点击:

Laravel 5.3 - Single Notification for User Collection (followers)

phplaravellaravel-5laravel-5.3

提问by Wonka

When I have a single notifiableuser, a single entry in the notificationstable is inserted, along with a mail/smssent which is perfectly working via channels.

当我只有一个notifiable用户时,notifications会插入表中的一个条目,以及一个通过渠道完美工作的mail/sms发送。

The issue is when I have a usercollection, a list of 1k users following me, and I post an update. Here is what happens when using the Notifiabletrait as suggested for multi-user case:

问题是当我有一个user收藏集,有 1k 个关注我的用户列表,然后我发布更新时。以下是Notifiable按照多用户案例的建议使用trait时会发生的情况:

  1. 1k mails/smssent (issue is not here)
  2. 1k notification entries added to the DB's notificationstable
  1. 1k mails/sms发送(问题不在这里)
  2. 1k 通知条目添加到数据库的notifications

It seems that adding 1k notifications to the DB's notificationstable is not an optimal solution. Since the toArraydata is the same, and everything else in the DB's notificationstable is the same for 1k rows, with the onlydifference being the notifiable_idof the usernotifiable_type.

似乎向数据库notifications表中添加 1k 通知不是最佳解决方案。由于该toArray数据是一样的,和一切在数据库中的notifications表是1K行相同,与作为区别notifiable_idusernotifiable_type

An optimal solution out of the box would be:

开箱即用的最佳解决方案是:

  1. Laravel would pick up the fact that it's an arraynotifiable_type
  2. Save a singlenotification as notifiable_typeuser_arrayor userwith notifiable_id0 (zero would only be used to signify it's a multi notifiable user)
  3. Create/Use another table notifications_readusing the notification_idit just created as the foreign_keyand insert 1k rows, of just these fields:

    notification_idnotifiable_idnotifiable_typeread_at

  1. Laravel 会发现它是一个 arraynotifiable_type
  2. 单个通知另存为0notifiable_typeuser_arrayuser使用notifiable_id0(零仅用于表示它是多通知用户)
  3. 创建/使用另一台notifications_read使用notification_id它刚创建的foreign_key不仅仅是这些领域,并插入1K行:

    notification_idnotifiable_idnotifiable_typeread_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/smsnotifications, 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 notificationstable

第 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 Notifiabletrait, you could create a custom channel.

是的,我猜您是对的,使用默认Notifiable特征,您可以创建自定义频道

You can check the Illuminate\Notifications\Channels\DatabaseChannelclass 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 HasDatabasePivotNotificationstrait (or similar name) to your own Notifiabletrait.

希望这有助于创建一个带有数据透视表的新频道。此外,HasDatabasePivotNotifications为您自己的Notifiable特征实现特征(或类似名称)。