Using soft deleted models in Laravel jobs
The Laravel queue worker discards any jobs where the model has been deleted by default. This can be a problem if you want to soft delete models in your application. In this article, I will show you how to use soft deleted models in Laravel jobs.
During my job I recently stumbled upon the problem that I needed to soft delete a model but also wanted to synchronize this deletion to a third party provider. Since the communication to the third party provider is done via HTTP requests, I wanted to use Laravel's queue system to make sure that the synchronization is done in the background. But the deletion of the model should still be done immediately to provide a instant feedback to the end user. After some research I found two possible ways to solve this problem. In this article I will show you both ways and explain the pros and cons of each solution.
Removing the SerializesModels
trait
By default all generated jobs use the SerializesModels
trait. This trait ensures only the model's id is saved on
job serialization instead of the whole model. When the job is picked up by the queue worker, the model is reloaded from the database using the id.
When the model is soft deleted in the meantime, the queue worker will not be able to find the model and discard the job.
By removing this trait you serialize the whole model instead of just the id. This way you get a copy of the model at the time the job was created. This means that the model will not be reloaded from the database when the job is picked up by the queue worker. You can use this option even if you force delete the model.
But depending on the model size or the loaded relations this can be a large payload and there are some limits in payload size depending on the queue driver.
Overwriting getQueryForModelRestoration
method
After some digging I found the unserialization of the model uses the getQueryForModelRestoration
method in the
SerializesAndRestoresModelIdentifiers
class. By overwriting this method in your job you can ensure the trashed model
are not filtered out.
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class DeleteUserJob implements ShouldQueue;
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function handle() {
// ...
}
protected function getQueryForModelRestoration($model, $ids): Builder
{
return $model->newQueryForRestoration($ids)->withoutGlobalScope(SoftDeletingScope::class);
}
}
This way you can still use the SerializesModels
trait and only the trashed models will be restored from the database.
However this solution only works if you soft delete the model. If you force delete the model, the model will not be found.