Laravel 5.5 — пользовательские теги заданий Horizon для прослушивателя событий в очереди

В документации для Horizon упоминается, что настраиваемые теги могут быть добавлены к прослушивателям событий в очереди. Однако я не могу найти способ получить свой экземпляр события, содержащий нужные мне данные. В приведенном примере используется подсказка типа, чтобы извлечь соответствующую модель из контейнера службы и присвоить ее переменной экземпляра в конструкторе, а затем использовать эту переменную экземпляра в методе tags() для получения данных о конкретном экземпляре модели, над которым выполняется операция.

Однако при выполнении этого в прослушивателе событий в очереди это не работает. На самом деле конструктор вообще никогда не вызывается из-за того, что модель сериализуется и «повторно гидратируется», когда дело доходит до выполнения. Таким образом, подсказка типа в конструкторе ничего не делает, и tags() вызывается перед handle(), поэтому я не могу получить доступ к объекту события, который я слушаю.

Кто-нибудь знает, как я могу получить информацию о событии в теге в этой ситуации?

Обновление:

Событие, вызванное в контроллере:

event(new PostWasCreated($user, $post));

Событие PostWasCreated:

<?php
namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use App\User;
use App\Post;

class PostWasCreated
{
    use InteractsWithSockets, SerializesModels;

    public $user;
    public $post;

    public function __construct(User $user, Post $post)
    {
        $this->user = $user;
        $this->post = $post;
    }

    public function broadcastOn()
    {
        return new PrivateChannel('channel-name');
    }
}

Прослушиватель PostWasCreatedNotificationSend:

<?php
namespace App\Listeners;

use App\Events\PostWasCreated;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class PostWasCreatedNotificationSend implements ShouldQueue
{
    protected $event;
    public $queue = 'notifications'; // Adds queue name

    public function __construct(PostWasCreated $event)
    {
      $this->event = $event;
      // Does NOT add queue tag
      $this->queueTags = ['post: ' . $this->event->post->id];
    }

    public function tags()
    {
      return $this->queueTags;
    }

    public function handle(PostWasCreated $event)
    {
      // handle event here...
    }
}

Проблема в том, что $this->queueTags никогда не назначается, поэтому в Horizon нет тегов для этого прослушивателя в очереди... (хотя имя очереди отображается, но нам также нужны теги).


person Wonka    schedule 30.03.2018    source источник
comment
Если я не ошибаюсь, класс, который на самом деле поставлен в очередь, — это CallQueuedListener, а не прослушиватель, который вы определили (см. Диспетчер для справки). Поэтому я думаю, что либо документация вводит в заблуждение, либо недостающую функциональность следует считать ошибкой.   -  person Namoshek    schedule 30.03.2018
comment
Horizon извлекает событие из задания CallQueuedListener для поиска любых тегов. Из этого описания сложно понять, что происходит. Не могли бы вы показать код события, которое вы пытаетесь поставить в очередь? Проявляет ли он черту SerializesModels?   -  person Cy Rossignol    schedule 31.03.2018
comment
@CyRossignol Извините за задержку, я разместил обновленный код, где он воспроизводим.   -  person Wonka    schedule 07.04.2018


Ответы (1)


Horizon собирает все теги перед даже помещением задания в очередь, поэтому мы не можем полагаться на какие-либо значения, которые задание не знает до его выполнения. В этом случае задание знает User и Post, потому что мы передаем их для инициализации события.

Для прослушивателей в очереди система тегов проверяет наличие тегов как в объекте события, так и в классе прослушивателя. Как описано в вопросе, невозможно установить теги с динамическими данными для прослушивателя, поскольку обработчик выполняется после, когда Horizon извлекает задание из очереди. Мы можем только объявить статические теги для прослушивателя, которые Horizon объединит* с тегами события:

class PostWasCreatedNotificationSend implements ShouldQueue 
{
    ...
    public function tags() 
    {
        return [ 'listener:' . static::class, 'category:posts' ];
    }
}

С объектом события Horizon пытается автоматически сгенерировать теги для любых членов модели Eloquent. Например, Horizon создаст следующие теги для события PostWasCreated:

  • $event->user App\User:<id>
  • $event->post App\Post:<id>

Мы можем переопределить это поведение и сообщить Horizon, какие теги установить для события, определив метод tags(), как показано выше:

class PostWasCreated 
{
    ...
    public function tags() 
    {
        return [ 'post:' . $this->post->id ];
    }
}

Обратите внимание, что на момент написания статьи Horizon не будет автоматически создавать теги для моделей, если событие или прослушиватель предоставляет теги вручную.

Проблема в том, что $this->queueTags никогда не назначается, поэтому в Horizon нет тегов для этого прослушивателя в очереди... (хотя имя очереди отображается, но нам также нужны теги).

Horizon не создает теги для каждого свойства; автоматическая пометка работает только для тех, которые содержат модели Eloquent (и, как правило, не для прослушивателя).


*Если событие также используется для трансляции (оно реализует ShouldBroadcast), дополнительное задание, созданное для публикации сообщения, не наследует никакие теги, предоставленные слушателем.

person Cy Rossignol    schedule 07.04.2018
comment
Спасибо, что вернулись к этому, это работает, как вы описали, когда теги установлены в событии, а слушатель поставлен в очередь. Странно, однако, что для уведомлений динамические теги работают, но не для самого прослушивателя событий. Я думал, что он должен работать с уведомлениями, заданиями и прослушивателями событий, поэтому все еще немного озадачен тем, почему он работает для уведомлений, а не для прослушивания событий динамически:/ - person Wonka; 07.04.2018
comment
@Wonka - Давайте посмотрим, понимаю ли я, что вы говорите ... Job, Event или Notification, а также любые модели или данные о них существуют до того, как они попадут в очередь, когда Horizon присваивает теги. Однако событие listener не содержит никаких данных... оно потребляет их из элемента, извлеченного из очереди, поэтому мы не можем использовать какие-либо из этих данных в тегах прослушивателя. Я понял, о чем вы спрашивали? - person Cy Rossignol; 07.04.2018
comment
Я так думаю да. Я специально пробовал с событием, которое SerializedModels и обычный (не поставленный в очередь) слушатель, у которого есть метод обработки, который запускает очередь Notification. В очереди Notification работают динамические теги. Я обновил последний фрагмент кода для слушателя в своем вопросе, чтобы он соответствовал тому, как выглядят рабочие динамические рабочие теги Notification. В основном обновлен конструктор и добавлен метод тегов, но эта настройка не работает для прослушивателя, только для уведомления (уведомление использует Queueable). Так что я не уверен, что это ошибка или я делаю что-то не так.. - person Wonka; 07.04.2018
comment
@Wonka - согласен - поначалу это сбивает с толку ... Я бы не назвал это ошибкой ... скорее ограничением текущей версии Horizon. Слушатели работают немного иначе, чем другие виды заданий, потому что система очередей разрешает их из контейнера (остальные воссоздаются из сериализованных данных, хранящихся в очереди), поэтому мы не можем внедрять модели/данные через конструктор. Нам нужно полагаться на теги событий для пометки заданий прослушивателя в очереди с помощью этих динамических данных. - person Cy Rossignol; 09.04.2018