4
votes

I want to create GUI for failed_jobs and associate them with other tables record. So user could know which jobs property value failed during job handle and could be retried.

Laravel Job has a function failed(\Exception $exception) but it is called after the exception but before the record is saved in the failed_jobs table.

Also Laravel has Queue:failing(FailedJob $job) event but there I have only serialized job but not failed_jobs.

Did anyone run into similar problem? Is there any relation with processed job and failed one?

1

1 Answers

0
votes

After much fussing, I accomplished this by embedding the related model within the Exception that is stored to the database. You could easily do similar but store only the id within the exception and use it to look up the model later. Up to you...

Wherever the exception that fails the job occurs:

try {
    doSomethingThatFails();
} catch (\Exception $e) {
    throw new MyException(OtherModel::find($id), 'Some string error message.');
}

app/Exceptions/MyException.php:

<?php

namespace App\Exceptions;

use App\Models\OtherModel;
use Exception;

class MyException extends Exception
{
    /**
     * @var OtherModel
     */
    private $otherModel = null;

    /**
     * Construct
     */
    public function __construct(OtherModel $otherModel, string $message)
    {
        $this->otherModel = $otherModel;

        parent::__construct(json_encode((object)[
            'message' => $message,
            'other_model' => $otherModel,
        ]));
    }

    /**
     * Get OtherModel
     */
    public function getOtherModel(): ?object
    {
        return $this->otherModel;
    }
}

That will store a string containing an object to the exception column on the failed_jobs table. Then you just need to decode that later on...

app/Models/FailedJob (or just app/FailedJob):

    /**
     * Return the human-readable message
     */
    protected function getMessageAttribute(): string
    {
        if (!$payload = $this->getPayload()) {
            return 'Unexpected error.';
        }

        $data = $this->decodeMyExceptionData();

        return $data->message ?? '';
    }

    /**
     * Return the related record
     */
    protected function getRelatedRecordAttribute(): ?object
    {
        if (!$payload = $this->getPayload()) {
            return null;
        }

        $data = $this->decodeMyExceptionData();

        return $data->other_model ?? null;
    }

    /**
     * Return the payload
     */
    private function getPayload(): ?object
    {
        $payload = json_decode($this->payload);

        return $payload ?? null;
    }

    /**
     * Return the data encoded in a WashClubTransactionProcessingException
     */
    private function decodeMyExceptionData(): ?object
    {
        $data = json_decode(preg_replace('/^App\\\Exceptions\\\WashClubTransactionProcessingException: ([^\n]*) in.*/s', '$1', $this->exception));

        return $data ?? null;
    }

anywhere:

$failedJob = FailedJob::find(1);

dd([
    $failedJob->message,
    $failedJob->related_record,
]);