Laravel 5.4, 5.3 : customize or extend notifications - database model

本秂侑毒 提交于 2019-11-27 09:41:52
BassMHL

To create a custom Notification Channel:

First, create a Class in App\Notifications for example:

<?php

namespace App\Notifications;

use Illuminate\Notifications\Notification;

class CustomDbChannel 
{

  public function send($notifiable, Notification $notification)
  {
    $data = $notification->toDatabase($notifiable);

    return $notifiable->routeNotificationFor('database')->create([
        'id' => $notification->id,

        //customize here
        'answer_id' => $data['answer_id'], //<-- comes from toDatabase() Method below
        'user_id'=> \Auth::user()->id,

        'type' => get_class($notification),
        'data' => $data,
        'read_at' => null,
    ]);
  }

}

Second, use this channel in the via method in the Notification class:

<?php

namespace App\Notifications;

use Illuminate\Notifications\Notification;

use App\Notifications\CustomDbChannel;

class NewAnswerPosted extends Notification
{
  private $answer;

  public function __construct($answer)
  {
    $this->answer = $answer;
  }

  public function via($notifiable)
  {
    return [CustomDbChannel::class]; //<-- important custom Channel defined here
  }

  public function toDatabase($notifiable)
  {
    return [
      'type' => 'some data',
      'title' => 'other data',
      'url' => 'other data',
      'answer_id' => $this->answer->id //<-- send the id here
    ];
  }
}

Create and use your own Notification model and Notifiable trait and then use your own Notifiable trait in your (User) models.

App\Notifiable.php:

namespace App;

use Illuminate\Notifications\Notifiable as BaseNotifiable;

trait Notifiable
{
    use BaseNotifiable;

    /**
     * Get the entity's notifications.
     */
    public function notifications()
    {
        return $this->morphMany(Notification::class, 'notifiable')
                            ->orderBy('created_at', 'desc');
    }
}

App\Notification.php:

namespace App;

use Illuminate\Notifications\DatabaseNotification;

class Notification extends DatabaseNotification
{
    // ...
}

App\User.php:

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    // ...
}

Unlike "Bassem El Hachem", I wanted to keep the database keyword in the via() methods.

So in addition to a custom DatabaseChannel, I also wrote my own ChannelManager that returns my own DatabaseChannel in the createDatabaseDriver() method.

In my apps' ServiceProvider::register() method, I overwrote the singleton for the original ChannelManager class to return my custom manager.

An example for @cweiske response.

If you really need extends the Illuminate\Notifications\Channels\DatabaseChannel not creating a new Channel you can:

Extends the channel:

<?php

namespace App\Notifications;

use Illuminate\Notifications\Channels\DatabaseChannel as BaseDatabaseChannel;
use Illuminate\Notifications\Notification;

class MyDatabaseChannel extends BaseDatabaseChannel
{
    /**
     * Send the given notification.
     *
     * @param  mixed  $notifiable
     * @param  \Illuminate\Notifications\Notification  $notification
     * @return \Illuminate\Database\Eloquent\Model
     */
    public function send($notifiable, Notification $notification)
    {
        $adminNotificationId = null;
        if (method_exists($notification, 'getAdminNotificationId')) {
            $adminNotificationId = $notification->getAdminNotificationId();
        }

        return $notifiable->routeNotificationFor('database')->create([
            'id' => $notification->id,
            'type' => get_class($notification),
            'data' => $this->getData($notifiable, $notification),

            // ** New custom field **
            'admin_notification_id' => $adminNotificationId,

            'read_at' => null,
        ]);
    }
}

And register the Illuminate\Notifications\Channels\DatabaseChannel on application container again:

app\Providers\AppServiceProvider.php

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(
            Illuminate\Notifications\Channels\DatabaseChannel::class,
            App\Notifications\MyDatabaseChannel::class
        );
    }
}

Now when the Illuminate\Notifications\ChannelManager try createDatabaseDriver will return your registered database driver.

More one option to solve this problem!

In my opinion laravel notifications are notyet ready and with very limited functionalities.

Anyway, i solved a similar problem by customizing notification class:

create the class for this action:

artisan make:notification NewQuestion

inside it:

public function __construct($user,$question)
    {
        $this->user=$user;
        $this->question=$question;
    }


...

    public function toDatabase($notifiable){
        $data=[
            'question'=>$this->(array)$this->question->getAttributes(),
            'user'=>$this->(array)$this->user->getAttributes()
        ];

        return $data;
    }

then you can access proper data in view or controller like this:

@if($notification->type=='App\Notifications\UserRegistered')
<a href="{!!route('question.show',$notification->data['question']['id'])!!}">New question from {{$notification->data['user']['name']}}</a>
@endif
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!