Удаление SerializesModels в прослушивателях событий в очереди не вступает в силу при насмешках над модульными тестами.

В настоящее время я работаю с прослушивателями событий Laravel в очереди на событие saving модели Eloquent. В моем классе событий я установил общедоступное свойство, содержащее мою модель, к которой я затем могу получить доступ внутри объекта $event в моем слушателе через $event->myModel.

Метод handle() моего слушателя сравнивает старые значения с новыми, поэтому я удалил Illuminate\Queue\SerializesModels в своем классе событий. Это отлично работает, и теперь я могу сравнивать старые значения с новыми.

Проблема в тестировании. Я делаю тест, который проверяет, отличается ли старое и новое значение, которое я получаю в методе handle() моего слушателя. Однако теперь $event->myModel->getOriginal() возвращает пустой массив. Это происходит только при тестировании.

Это ошибка?

Я попытался установить public $originalModelProps в своем классе событий, однако он возвращает пустое значение, когда вызывается метод handle() моего слушателя.

Я не использую Illuminate\Queue\SerializesModels или какой-либо трейт/класс/интерфейс с именем SerializesModels в своем коде.

События/Модели/MyModel/MyModelSavedEvent.php

<?php

namespace App\Events\Models\MyModel;

use App\Models\MyModel;
use Illuminate\Queue\SerializesModels;

class MyModelSavedEvent
{
    /**
     *
     * @var \App\Models\MyModel
     */
    public $myModel;

    public $originalAccessLevelProps;

    /**
     * Create a new event instance.
     * 
     * @param \App\Models\MyModel
     *
     * @return void
     */
    public function __construct(MyModel $myModel)
    {
        $this->myModel = $myModel;
        $this->originalAccessLevelProps = $this->myModel->getOriginal('level');
    }
}

Слушатели/Модели/MyModel/MyModelSavedEventListener.php

<?php

namespace App\Listeners\Models\MyModel;

use Log;
use App\Models\MyModel;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\Events\Models\MyModel\MyModelSavedEvent;

class MyModelSavedEventListener implements ShouldQueue
{
    /**
     * The queue connection that should handle the job.
     *
     * @var string
     */
    public $connection = 'sqs';

    /**
     * The number of times the job may be attempted.
     *
     * @var int
     */
    public $tries = 3;

    /**
     * The number of seconds the job can run before timing out.
     *
     * @var int
     */
    public $timeout = 60;

    public function __construct()
    {
        // ..
    }

    /**
     * This job should only be queued if the level of myModel
     * has been updated.
     *
     * @param  \App\Events\Models\MyModel\MyModelSavedEvent $event
     * 
     * @return bool
     */
    public function shouldQueue(MyModelSavedEvent $event)
    {
        $prevLevel = $event->myModel->getOriginal('level');
        $currLevel = $event->myModel->level;

        return $prevLevel !== $currLevel;
    }

    public function handle(MyModelSavedEvent $event): void
    {
        $prevLevel = $event->myModel->getOriginal('level');
        $currLevel = $event->myModel->level;

        Log::info([
            'Running handle!',
            $event->myModel->getOriginal(), // This returns an empty array
            $event->originalAccessLevelProps, // This is null
            $event->myModel->id, // This returns a string
            $prevLevel, // This returns null
            $currLevel // This contains the latest value
        ]);

        // ..
    }
}

tests/app/Listeners/Models/MyModel/MyModelSavedEventListener.php

<?php

public function testSample()
{
    Queue::fake();

    $myModel = MyModel::where(...)->first();

    Queue::assertNothingPushed();

    // This will trigger the event
    $myModel->level = 'new level';
    $myModel->save();

    Queue::assertPushedOn('sqs', MyModelSavedEventListener::class);
}

Я ожидал, что аналогично тому, как это работает вне теста, я смогу сравнить старые и новые значения.

Изменить. Событие запускается через свойство $dispatchesEvents MyModel:

/**
 * The event map for the model.
 *
 * @var array
 */
protected $dispatchesEvents = [
    'saved' => \App\Events\Models\MyModel\MyModelSavedEvent::class,
];

Обновление: выяснилось, что моя модель действительно не сериализуется. Просто getOriginal() по-прежнему возвращает пустой массив при вызове в методе слушателя handle()


person Dreadnaut    schedule 26.08.2019    source источник
comment
Как запускается событие?   -  person miken32    schedule 26.08.2019
comment
Обратите внимание, что прослушиватель событий, используемый в вашем тестовом классе, имеет то же имя, что и сам тестовый класс MyModelSavedEventListener   -  person Salim Djerbouh    schedule 26.08.2019
comment
Привет @ miken32, я обновил вопрос. Событие запускается через свойство MyModel $dispatchesEvents.   -  person Dreadnaut    schedule 27.08.2019
comment
@CaddyDZ, возможно, я перепутал имена классов, но только потому, что мне пришлось переименовать их для большей конфиденциальности :) Никаких проблем с именами, все работает нормально, только сериализация.   -  person Dreadnaut    schedule 27.08.2019


Ответы (1)


SerializesModel используется для экономии места за счет сериализации идентификатора модели и некоторой метаинформации при сохранении задания в очереди.

Когда вы передаете экземпляр модели в задание, которое не использует SerializesModel, экземпляр по-прежнему сериализуется с помощью PHP serialize() и восстанавливается при вызове слушателя.

Так что, возможно, попробуйте передать интересующие вас свойства напрямую MyModelSavedEvent::dispatch()

person Olivenbaum    schedule 26.08.2019
comment
Привет @timw, как мне это сделать с помощью прослушивателей событий в очереди? Модель запускает событие вручную через свойство MyModel $dispatchesEvents. - person Dreadnaut; 27.08.2019
comment
@Dreadnaut, вы можете запустить другое событие из прослушивателя моделей и поставить в очередь только последующее событие. Затем вы можете передать любые свойства, которые вам нравятся. У меня также были некоторые проблемы с прослушивателями моделей в очереди. Например MyModelCompareOriginal::dispatch($old, $original). - person Olivenbaum; 27.08.2019