'datetime', 'finished_at' => 'datetime', 'details' => 'array', ]; public function triggeredByUser() { return $this->belongsTo(\App\Models\User::class, 'triggered_by_user_id'); } /** * Wraps a unit of cron work, recording start/finish/status and any * exception. Returns whatever the work returns; the resulting * UnifiCronRun row is returned via the $run reference param. */ public static function record(string $command, string $triggeredBy, ?int $userId, callable $work): self { $run = static::create([ 'command' => $command, 'triggered_by' => $triggeredBy, 'triggered_by_user_id' => $userId, 'started_at' => now(), 'status' => 'running', ]); try { $details = $work($run); // Caller can return a status string ("skipped", "partial", // etc.) by sticking it under the 'status' key in details. // Default = succeeded. $status = is_array($details) && isset($details['status']) ? $details['status'] : 'succeeded'; $run->update([ 'finished_at' => now(), 'status' => $status, 'details' => is_array($details) ? array_diff_key($details, ['status' => null]) : null, ]); } catch (\Throwable $e) { $run->update([ 'finished_at' => now(), 'status' => 'failed', 'details' => [ 'error' => $e->getMessage(), 'class' => $e::class, 'file' => $e->getFile() . ':' . $e->getLine(), ], ]); throw $e; } return $run->refresh(); } }