Laravel 5.4, 5.3: настройка или расширение уведомлений - модель базы данных

ИМХО, текущий канал базы данных для сохранения уведомлений в Laravel действительно плохой дизайн:

  • Нельзя использовать каскады внешних ключей для элементов, например, для очистки уведомлений об удаленных элементах.
  • Поиск пользовательских атрибутов в data столбец (приведенный к массиву) не является оптимальным

Как бы вы пошли о продлении DatabaseNotification Модель в упаковке продавца?

Я хотел бы добавить столбцы event_id, question_id, user_id (пользователь, создавший уведомление) и т. д. по умолчанию notifications Таблица

Как вы переопределяете send функция, чтобы включить больше столбцов?

В:

vendor/laravel/framework/src/Illuminate/Notifications/Channels/DatabaseChannel.php

Код:

class 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')->create([
        'id' => $notification->id,
        'type' => get_class($notification),

      \\I want to add these
        'user_id' => \Auth::user()->id,
        'event_id' => $notification->type =='event' ? $notification->id : null, 
        'question_id' => $notification->type =='question' ? $notification->id : null,
      \\End adding new columns

        'data' => $this->getData($notifiable, $notification),
        'read_at' => null,
    ]);
 }
}

6 ответов

Решение

Чтобы создать собственный канал уведомлений:

Сначала создайте класс в App\Notifications, например:

<?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,
    ]);
  }

}

Во-вторых, используйте этот канал в via метод в классе уведомлений:

<?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
    ];
  }
}

Создать и использовать свой собственный Notification модель и Notifiable черта, а затем используйте свою собственную черту Уведомляемость в ваших (пользовательских) моделях.

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;

    // ...
}

Пример ответа @cweiske.

Если вам действительно нужно продлить Illuminate\Notifications\Channels\DatabaseChannel не создавая новый канал, вы можете:

Расширяет канал:

<?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,
        ]);
    }
}

И зарегистрируйте Illuminate\Notifications\Channels\DatabaseChannel снова в контейнере приложения:

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
        );
    }
}

Теперь, когда Illuminate\Notifications\ChannelManager пытаться createDatabaseDriver вернет ваш зарегистрированный драйвер базы данных.

Еще один вариант решения этой проблемы!

В отличие от "Bassem El Hachem", я хотел сохранить database Ключевое слово в via() методы.

Так в дополнение к обычаю DatabaseChannelЯ тоже написал свой ChannelManager это возвращает мой собственный DatabaseChannel в createDatabaseDriver() метод.

В моих приложениях ServiceProvider::register() метод, я переписал синглтон для исходного класса ChannelManager, чтобы вернуть мой пользовательский менеджер.

Вы можете ввести значения новых столбцов на уровне модели, прослушиваяcreatingсобытие.

  1. Установите уведомление.
      namespace App\Notifications;

use Illuminate\Notifications\Notification;

class YourNotification extends Notification
{
    // ...

    public function toArray($notifiable): array
    {
        return [
            'new_column' => 'value',
            'serializable_data' => $serializable_data,
        ];
    }
}
  1. Создайте свою собственную модель уведомлений и отредактируйте то, что вставляется.
      namespace App\Models;

use Illuminate\Notifications\DatabaseNotification;

class Notification extends DatabaseNotification
{
    protected static function booted(): void
    {
        static::creating(function (Notification $notification) {
            $data = $notification->data;

            $notification->new_column = $data['new_column'];
            unset($data['new_column']);

            $notification->data = $data;
        });
    }
}
  1. Переопределить связь в модели пользователя.
          public function notifications()
    {
        return $this->morphMany(Notification::class, 'notifiable')->orderBy('created_at', 'desc');
    }

По моему мнению, уведомления Laravel еще не готовы и имеют очень ограниченные функциональные возможности.

В любом случае, я решил похожую проблему, настроив класс уведомлений:

создайте класс для этого действия:

artisan make:notification NewQuestion

внутри него:

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;
    }

тогда вы можете получить доступ к нужным данным в представлении или контроллере следующим образом:

@if($notification->type=='App\Notifications\UserRegistered')
<a href="{!!route('question.show',$notification->data['question']['id'])!!}">New question from {{$notification->data['user']['name']}}</a>
@endif
Другие вопросы по тегам