文章轉發自專業的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 消息通知的案例。