У меня проблема с заданием в очереди Laravel 5.6. Я передаю 3 аргумента конструктору. На данный момент я проверил, и все аргументы имеют данные. Но когда я запускаю задание, один из них пуст в обработчике.
Это функция, которая отправляет задание:
public function send() {
$event = Evento::find($id);
$subscribers = ParticipantesEvento::join('users', 'participantes_eventos.user_id', '=', 'users.id')
->select('users.name', 'users.email')
->where('evento_id', '=', $evento->id)
->get();
$certificate = Certificado::where('evento_id', '=', $evento->id)->first();
dispatch(new SendCertificatesByEmail($subscribers, $event, $certificate));
return redirect()->back()->with('success', 'Fazendo os paranauê...');
}
Это работа:
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
class SendCertificatesByEmail implements ShouldQueue {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $subscribers, $events, $certificate;
public function __construct($s, $e, $c) {
$this->subscribers = $s;
$this->events = $e;
$this->certificate = $c;
print_r($this->subscribers); // Have data
print_r($this->events); // Have data
print_r($this->certificate); // Have data
}
public function handle() {
print_r($this->subscribers); // IS EMPTY!!!
print_r($this->events); // Have data
print_r($this->certificate); // Have data
}
}
Хотя в документации Laravel сказано объявлять переменные как защищенные в задании, я уже пробовал использовать их как общедоступные. Та же проблема. У меня уже закончились идеи. Что я делаю не так??
@GTCrais Пробовал. Не работает... =/
Если это поможет... Обе переменные являются информацией из базы данных. Единственная разница между проблемной переменной и другими состоит в том, что это коллекция со многими подписчиками. Два других имеют по одному регистру (одно событие и один сертификат).
Попробуйте $this->subscribers = "whatever";
подтвердить/устранить проблему в параметре $s
.
Я уже делал этот тест. При присвоении любого «любого» значения $this->subscribers
в конструкторе значение обычно отображается в обработчике.
@GTCrais Я включил в вопрос происхождение данных, переданных в задание.
Хм... ну, я предполагаю, что по какой-то причине он не может сериализовать подписчиков. Проверьте черту SerializesModels
и попробуйте вручную сериализовать/десериализовать подписчиков, чтобы посмотреть, что произойдет... это единственное, что я могу придумать. Однако он должен иметь возможность сериализовать коллекцию моделей Eloquent...
Ваша коллекция красноречивых моделей неправильно структурирована. Из-за этого Laravel не может правильно сериализовать их для отправки и десериализовать при запуске задания.
Когда вы отправляете красноречивую модель (или набор моделей) в задание очереди, Laravel сериализует идентификаторы моделей только при отправке задания. Когда задание затем обрабатывается, оно принимает идентификаторы и извлекает данные из базы данных.
В этом случае ваша $subscribers
красноречивая коллекция ParticipantesEvento
моделей. Кроме того, из-за вашего запроса единственными данными, которые есть у ваших моделей, являются имя и адрес электронной почты пользователя. Итак, когда Laravel попытается сериализовать ключи этой коллекции, он ничего не найдет.
Даже если вы обновите свой запрос, чтобы включить поле participantes_eventos.id
в модель, при выполнении задания ваше свойство subscribers
будет новой коллекцией моделей ParticipantesEvento без пользовательских данных, которые вы включили из исходного запроса.
Ваш код показывает, что вы действительно хотите, чтобы ваш $subscribers
был пользователями, которые присоединены к событию. Предполагая, что у вас есть настроенные красноречивые отношения, вы можете просто сделать это:
$subscribers = $event->users;
Если у вас нет настройки отношений, добавьте это в свою модель Evento
. Это устанавливает отношение многие ко многим от событий к пользователям через таблицу participantes_eventos
.
public function users()
{
return $this->belongsToMany(User::class, 'participantes_eventos');
}
То же самое можно сделать и с сертификатом. Добавьте этот метод в свою модель Evento
, чтобы настроить отношение один к одному от события к сертификату:
public function certificate()
{
return $this->hasOne(Certificado::class);
}
А затем используйте его так:
$certificate = $event->certificate;
У меня нет этой структуры. У меня есть таблица eventos
, в которой есть «id», «имя», «описание» и т. д. У меня есть таблица users
, в которой есть «id», «имя» и «электронная почта». И у меня есть таблица participantes_evento
, которая "объединяет" подписчиков каждого "evento" системы, имеющая поля "id", "evento_id" и "user_id". Использовать $subscribers = $evento->users;
в этой структуре не получится (да, я знаю, что это не самая правильная структура, но с этим мне приходится иметь дело).
Я только что нашел обходной путь для этой проблемы.... но я постараюсь сделать это и вернуться, если это сработает.
БИНГО! Это отлично работает, чувак! Большое спасибо за помощь! Просто добавьте return
в функцию certificate()
, иначе будет выдано исключение. Я отмечу ответ как правильный! Спасибо еще раз!
@AndréRaubach Тьфу, извините за опечатку и спасибо за редактирование. Но да, то, что вы описали, — это отношения «многие ко многим», где таблица participantes_evento
— это сводная таблица, которая связывает многих пользователей со многими событиями, и наоборот. Отношения belongsToMany
— это то, как вы моделируете это в красноречии. Рад, что у вас все заработало!
Ну... кажется, что когда задание выполняется, SerializesModels повторяют запрос модели, и по какой-то причине все идет плохо. Я сделал обходной путь... В функции, с которой я отправляю задание, вместо того, чтобы передавать модель заданию, я создал массив с результатом. И передайте этот массив в задание. Таким образом, задание не будет повторять запрос, а просто прочитает полученный статический массив:
$inscritos = ParticipantesEvento::join('users', 'participantes_eventos.user_id', '=', 'users.id')
->select('users.name', 'users.email')
->where('evento_id', '=', $evento->id)
->get();
$insc = array();
foreach ($inscritos as $inscrito) {
$insc[] = $inscrito;
}
Но...
ВНИМАНИЕ:
Это может вызвать проблемы в некоторых сценариях. Как сказано в этой статье: https://gistlog.co/JacobBennett/e60d6a932db98985f160146b09455988 , преимущество повторного выполнения запроса SerializesModel заключается в получении обновленных данных из базы данных. Например: если пользователь изменит ваш адрес электронной почты или имя в своем регистре после, я отправлю статический массив в задание, когда задание будет выполнено, оно будет работать с устаревшей информацией о пользователе.
Тем не менее, на данный момент это помогло... пока я думаю о более элегантном решении этой проблемы. Спасибо всем, кто помог!
Можете просто попробовать поставить
$this->subscribers = clone $s;
, просто посмотреть, что получится?