Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add custom field to Laravel queued job records?

I have a working Laravel 5 queued job class called 'SendMyEmail' using the 'database' driver. The database 'jobs' table is filled with such dispatched jobs correctly.

I would like to show these jobs on a website and therefore I would like to add and save a value on a custom field called 'name' on these jobs records when they are constructed. (I would pass this name as a parameter to the SendMyEmail class constructor..)

Does anyone know how to do this?

like image 914
P. Prat Avatar asked Oct 02 '15 13:10

P. Prat


1 Answers

Alright so you want to keep a history of queued/processed jobs, right.

There isn't any built-in support for customizing the database fields.

See:

https://github.com/laravel/framework/blob/7212b1e9620c36bf806e444f6931cf5f379c68ff/src/Illuminate/Queue/DatabaseQueue.php#L170

http://i.imgur.com/nFciWi9.png

From my understanding this behaviour is intended, because you shouldn't really mess with the original 'jobs' table. It was designed to work stateless. That means that a job record is removed right after it has been processed.

If you want to keep track of your jobs (eg. history), you might just create a new Eloquent model and pass it to your jobs constructor. This is useful, in order to keep the original job and your history synchronized.


Alright let's start coding, shall we?

Create a new migration by entering:

php artisan make:migration create_jobs_history_table

Now open that migration and add the following column types.

database/migrations/ xyz_create_jobs_history_table:

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateJobsHistoryTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('jobs_history', function(Blueprint $table) {

            $table->bigIncrements('id');
            $table->unsignedInteger('user_id');
            $table->string('job', 40);
            $table->integer('status')->default(0);
            $table->timestamps();

            if (Schema::hasColumn('users', 'id'))
            {
                $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
            }

        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::disableForeignKeyConstraints();
        Schema::dropIfExists('jobs_history');
        Schema::enableForeignKeyConstraints();
    }
}

Explanation:

As you can see, we added three new types called user_id, job and status.

The user_id references the actual id of a user.

The job field is just a description/name of the job.

The status field represents the status. 0 = Not yet proccessed, 1 = Done


Now that our migration is ready, let us define a new model for it:

app/ JobHistory.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class JobHistory extends Model
{
    protected $table = 'jobs_history';

    protected $hidden = [];

}

Sweet. Now we can easily interact with our job history in our application.


It is time to create a job. Let's do so by using the following code:

app/Jobs/ ProvisionUser.php

<?php

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;

use App\User;
use App\JobHistory;

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

    protected $user;
    protected $history;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct(User $user, JobHistory $history)
    {
        $this->user = $user;
        $this->history = $history;

        // Set up our new history record.

        $this->history->user_id = $this->user->id;
        $this->history->job = 'Provison User';
        $this->history->status = 0;

        $this->history->save();
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        // Do other stuff here....

        // Once the job has finished, set history status to 1.
        $this->history->status = 1;
        $this->history->save();
    }
}

Explanation:

Here we included the User and JobHistory models. In our constructor we require both models and we set up a new history record.

The actual job is now synchronized with our new history record!

Good.

The handle() function is called while the job is being processed. Here we set the status to 1, once it has finished.

And lastly simply dispatch the job in your controller:

<?php

namespace App\Http\Controllers;

use Carbon\Carbon;

use App\User;
use App\JobHistory;
use App\Jobs\ProvisionUser;

class SomeController extends Controller
{
    public function provision()
    {
        $user = User::find(1);

        $job = (new ProvisionUser($user, new JobHistory))
            ->delay(Carbon::now()->addMinutes(1));

        dispatch($job);

        return view('provision');
    }
}

Explanation:

We pass an existing user and new job history to the constructor. After that we dispatch the delayed job.

Note: The delay is just for demonstrational purposes.

Open up your database and check your jobs_history table. As soon as your job was dispatched, the status of the corresponding history record should be 0. Once the artisan queue worker has processed the job, the history record status should be 1.

I tested this setup with Laravel 5.4 and I use the same logic in my application.

Happy coding!

like image 183
Steven Avatar answered Nov 20 '22 15:11

Steven