phplaraveleloquentmodeljobs

Saving a Newly Created Eloquent Model as a Job Property Prevents the Job from Being Handled


I have a Laravel job that is supposed to accept a newly created Eloquent model as a constructor attribute, then perform a save() operation on it in the handle() method. However, as soon as I try saving the model to the private property, the handle() method is not executed anymore. Here's the code:

MyController.php:

$category = new Category;
SaveModelJob::dispatch($category);

SaveModelJob.php

<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class SaveModelJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    private $model;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($model)
    {
        //
        dump('saving as property'); // this gets executed
        $this->model = $model; // <--- PROBLEMATIC CODE
        dump('saved as property'); // this also gets executed
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        dump("handling"); // <--- This stops being executed as soon as the PROBLEMATIC CODE above is added
        // a $this->model->save() operations is intended here
        

    }
}

In my real controller, some Model attributes are set before passing the $category to the SaveModelJob, but for simplicity's sake, I've removed most of the irrelevant code. To get some of the possible issues out of the way:

I have a suspicion that the Job object does not act like a regular class and the handle() method is reached much later, outside of the initial request lifecycle and this might have implications in the way the private property is accessed at a later time (especially when the queue driver is not "sync", but say, Redis), but I get no such errors in the log.

Do you know what the issue can be?


Solution

  • When you dispatch an object to the queue, the object will be saved into the queue, but if you’re passing an Eloquent Model, it will be referenced from the Database by its id (or primary key) instead of being saved entirely into the payload

    To avoid that using an unsaved model, just send it as an array.

    Controller

    $category = new Category;
    SaveModelJob::dispatch($category->toArray()); //or build the array from scratch
    

    SaveModelJob

    public function __construct($model)
    {
        //
        dump('saving as property');
        $this->model = Category::make($model);
        dump('saved as property');
    }
    

    If you use the same script for all your models, add another attribute for the class

    Controller

    $category = new Category;
    SaveModelJob::dispatch($category->toArray(), Category::class); //or build the array from scratch
    

    SaveModelJob

    public function __construct($model, $eloquentClass)
    {
        //
        dump('saving as property');
        $this->model = $eloquentClass::make($model);
        dump('saved as property');
    }