文章转发自专业的Laravel开发者社区,原始连接: https://learnku.com/laravel/t...
Laravel Notification 在 5.3 版本中被添加做为核心框架的扩展。它为咱们提供了一种简单易懂,表现力强的 API 去发送通知,包括各类预置发送渠道还有 Laravel 社区提供的各类 自定义渠道。php
其中一个重要的渠道是数据库通知,通知消息数据存储在数据库。下面是一个简单的例子,假设咱们有一个 InvitationNotification 类,里面有 toArray 方法用来生成通知消息的标题和详情:react
<?php namespace App\Notifications; use App\Channels\FCMChannel; use App\Models\Invitation; use App\Models\User; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Notification; class InvitationNotification extends Notification implements ShouldQueue { private $sender; private $invitation; public function __construct(User $sender, Invitation $invitation) { $this->sender = $sender; $this->invitation = $invitation; } public function via($notifiable) { return [FCMChannel::class, 'database']; } public function toArray($notifiable) { return [ 'title' => trans('notifications.new_invitation_title'), 'details' => trans('notifications.new_invitation_details', [ 'sender' => $this->sender->full_name, 'receiver' => $notifiable->full_name, 'relation' => $this->invitation->relation->name, 'invitation' => trans("mobile.invitation") ] ) ]; } }
从前面的代码能够看出,Laravel 数据库通道会提早把 toArray 或者 toDatabase 方法生成的通知消息数据存储进数据库,这在某些项目中也许又用,可是仍然会有一些限制:laravel
为了支持多种语言,我尝试过把键和参数的对应关系存储进数据库,读取通知数据时经过 trans 函数来转换 title 和 details。可是问题是传输的参数也可能须要要再去翻译。git
还有就是,咱们不能只是经过建立一条新的通知或者更新当前通知的内容来进行这些更改。github
当我为了找到最好的办法来解决这个问题而陷入僵局时,我有了另外一个想法,因而我构建了这个扩展包。这个方案依赖于重写 DatabaseChannel 把通知数据存储为序列化通知对象,而不是存储键和值数组。数据库
<?php namespace App\Channels; use App\Models\CustomDatabaseNotification; use Illuminate\Notifications\Notification; class DatabaseChannel extends \Illuminate\Notifications\Channels\DatabaseChannel { /** * Send the given notification. * * @param mixed $notifiable * @param \Illuminate\Notifications\Notification $notification * * @return \Illuminate\Database\Eloquent\Model */ public function send($notifiable, Notification $notification) { return $notifiable->routeNotificationFor('database', $notification)->create( $this->buildPayload($notifiable, $notification) ); } /** * Get the data for the notification. * * @param mixed $notifiable * @param \Illuminate\Notifications\Notification $notification * * @throws \RuntimeException * * @return Notification */ protected function getData($notifiable, Notification $notification) { return $notification; } /** * Build an array payload for the DatabaseNotification Model. * * @param mixed $notifiable * @param \Illuminate\Notifications\Notification $notification * * @return array */ protected function buildPayload($notifiable, Notification $notification) { return [ 'id' => $notification->id, 'type' => get_class($notification), 'data' => ['data' => serialize($this->getData($notifiable, $notification))], 'read_at' => null, 'serialized' => true ]; } }
我还重写了 DatabaseNotification 模型,并添加了数据字段的访问器,以便在序列化数据后对数据进行反序列化,而后提取通知对象,最后调用 toDatabase 或 toArray 方法来获取最终输出。数组
<?php namespace App\Models; class DatabaseNotification extends \Illuminate\Notifications\DatabaseNotification { public function getDataAttribute() { $data = $this->attributes['data']; if (isset($this->attributes['serialized']) && $this->attributes['serialized']) { $obj = unserialize($data['data']); if (method_exists($obj, 'toDatabase')) { return $obj->toDatabase($this->notifiable); } else { return $obj->toArray($this->notifiable); } } else { return $data; } } }
优势composer
经过使用这种方式,全部构建通知的代码都被移动到 toDatabase 或 toArray 方法来作处理。框架
安装方法
composer requires digitalcloud/reactive-notificationide
发布和迁移
php artisan vendor:publish provider="Digitalcloud\ReactiveNotification\ReactiveNotificationServiceProvider" php artisan migrate
用法
<?php namespace App; use Digitalcloud\ReactiveNotification\Traits\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use Notifiable; }
2. 从数据库更改传递的方法
或者 IlluminateNotificationsChannelsDatabaseChannel 到 DigitalcloudReactiveNotificationChannelsDatabaseChannel.
<?php namespace App\Notifications; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Notification; use Illuminate\Contracts\Queue\ShouldQueue; use Digitalcloud\ReactiveNotification\Channels\DatabaseChannel; class InvoicePaid extends Notification implements ShouldQueue { use Queueable; public function via($notifiable) { return [DatabaseChannel::class,'.../']; } public function toDatabase($notifiable){ return [ "title"=> trans("invoice_title"), "details"=> trans("invoice_details"), ]; } }
示例
<?php $user->notify(new InvoicePaid($invoice)); \App::setLocale("en"); $result = $user->notifications()->first()->data; //result will be [ "title" => "Invoice title", "details" => "Invoice details" ] \App::setLocale("ar"); $result = $user->notifications()->first()->data; //result will be [ "title" => "عنوان الفاتورة", "details" => "تفاصيل الفاتورة" ]
<?php namespace App\Notifications; use App\Models\Member; use Carbon\Carbon; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Digitalcloud\ReactiveNotification\Channels\DatabaseChannel; class BirthDayNotification extends CustomNotification implements ShouldQueue { use Queueable; private $member; public function __construct(Member $member) { $this->member = $member; } public function via() { return [DatabaseChannel::class]; } public function toArray($notifiable) { $date = Carbon::parse($this->member->birthday)->format("m-d"); if (today()->format("m-d") == $date) { $details = trans_choice('notifications.birth_day_today', $this->member->gender, ['name' => $this->member->name]); } elseif (today()->subDay()->format("m-d") == $date) { $details = trans_choice('notifications.birth_day_yesterday', $this->member->gender, ['name' => $this->member->name]); } else { $details = trans_choice('notifications.birth_day_old', $this->member->gender, ['name' => $this->member->name, "date" => $date]); } return [ 'title' => trans('notifications.birth_day_title'), 'details' => $details ]; } }
$user->notify(new BirthDayNotification($member)); $notification = $user->notifications()->first();
若是一个成员的生日是今天,结果消息则为 “今天是 John 的生日,但愿他过一个快乐生日!”
若是成员的生日是昨天,最终消息体会改成 “John 昨天庆祝了他的生日”。
Carbon::setTestNow(now()->addDay());
最后要说的,若是成员的生日是具体日期,最终消息体会改成 “John 在12月31日庆祝了他的生日”。
Carbon::setTestNow(now()->addMonths(3));
但愿你能喜欢使用这个包,并从列出的细节中获益。请继续关注其余有关高级 Laravel 消息通知的案例。