option('dry-run'); $limit = (int) $this->option('limit'); $now = now()->utc(); $candidates = NewsArticle::query() ->where('editorial_status', NewsArticle::EDITORIAL_STATUS_SCHEDULED) ->whereNotNull('published_at') ->where('published_at', '<=', $now) ->orderBy('published_at') ->limit($limit) ->get(['id', 'title', 'published_at']); if ($candidates->isEmpty()) { $this->line('No scheduled News articles due for publishing.'); return self::SUCCESS; } $published = 0; $errors = 0; foreach ($candidates as $candidate) { if ($dryRun) { $this->line(sprintf('[dry-run] Would publish News article #%d: "%s"', $candidate->id, $candidate->title)); continue; } try { DB::transaction(function () use ($candidate, $now, &$published): void { $article = NewsArticle::query() ->lockForUpdate() ->where('id', $candidate->id) ->where('editorial_status', NewsArticle::EDITORIAL_STATUS_SCHEDULED) ->whereNotNull('published_at') ->where('published_at', '<=', $now) ->first(); if (! $article) { return; } $article->forceFill([ 'editorial_status' => NewsArticle::EDITORIAL_STATUS_PUBLISHED, 'status' => 'published', 'published_at' => $article->published_at ?? $now, ])->save(); $published++; $this->line(sprintf('Published News article #%d: "%s"', $article->id, $article->title)); }); } catch (\Throwable $exception) { $errors++; Log::error('PublishScheduledNewsCommand failed', [ 'article_id' => $candidate->id, 'message' => $exception->getMessage(), ]); $this->error(sprintf('Failed to publish News article #%d: %s', $candidate->id, $exception->getMessage())); } } if (! $dryRun) { $this->info(sprintf('Done. Published: %d, Errors: %d.', $published, $errors)); } return $errors > 0 ? self::FAILURE : self::SUCCESS; } }