← All writing

Schedule Laravel articles with a timestamp instead of a permanent worker

Laravel 13Sources checked 2026-09-12

Apply the same rule to lists, direct URLs, feeds, and the sitemap.

A small publication wants one article to appear tomorrow morning. Running an always-on queue worker solely to flip a visibility flag adds an unnecessary operational dependency. A saved publication time can express the rule directly.

Put the rule in one query scope

public function scopePublished($query)
{
    return $query->whereNotNull('published_at')
        ->where('published_at', '<=', now());
}

A null time is a draft. A future time is scheduled. A time that has arrived is public. Apply the scope to lists, feeds, related articles, and the sitemap.

Protect direct URLs too

Hiding a draft from the list is not enough. The detail route must check the same publication condition and return 404 for a private article. A guessed numeric ID should not reveal tomorrow's content.

Authenticated previews need a separate access rule and noindex metadata. Do not weaken the public detail route just to make local review convenient.

Account for caches and downtime

The timestamp rule is evaluated when the application handles a request. If a whole page or CDN response is cached across the publication boundary, configure its lifetime or invalidation accordingly. The database rule cannot expire an unrelated external cache.

When the application returns after downtime, articles whose dates passed are visible. They retain their intended publication times. There is no replay loop rapidly sending a backlog of messages.

Test one draft, one future article, and one past article. Advance the clock and confirm the future article appears in every public surface. Repeat the request before and after to catch stale caches.

Laravel's query builder documentation provides the query primitives. A scheduled command is still useful for assigning dates to newly approved drafts, but already scheduled content does not need a worker.