Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

kylef-fotf's avatar

Observer and scheduled publish dates

In models that utilize a published_at attribute, what is a clean way to get an Observer to know that a record has passed it's published_at date and is now published? The main use case for this is I want to trigger a service function that should only apply to published records and not scheduled ones.

I know this is a logical issue. The published_at timestamp is updated in advance, so the updating event will not fire until it's been updated after the timestamp has passed. Do I need to rely on scheduled jobs to check for published records periodically? Or is there a cleaner way?

0 likes
3 replies
LaryAI's avatar
Level 58

Certainly! Here’s a clean way to handle this scenario.

Summary:
You need to trigger logic when a record becomes published (i.e., its published_at timestamp, set in advance, crosses the current time). Eloquent observers/events won't fire at publish time unless you use a real-time check.

The Clean Approach

Laravel Observers (like updated) will not fire again at the moment the scheduled time (published_at) "arrives"; they only fire when the model is saved/updated. Therefore, the only robust solution is to use scheduled jobs (i.e., Laravel Scheduler) to periodically check for records that have just reached their publish time.

Steps:

  1. Create a Scheduled Command to Check for Newly Published Records
  1. Add the Needed Field

Add a published_processed_at nullable datetime to your posts (or whatever model you're using), so you know which published records you've already processed.

// In your migration
$table->timestamp('published_processed_at')->nullable();
  1. Schedule the Command

In app/Console/Kernel.php:

protected function schedule(Schedule $schedule)
{
    $schedule->command('posts:check-published')->everyMinute();
}

Key Points

  • Observers: Great for instant model changes, not ideal for time-based transitions like this.
  • Scheduled Job: Provides reliable, decoupled processing at the moment the record "becomes" published.
  • Idempotency: Make sure the job can't double-process a record (hence the published_processed_at check).

Bonus: Event-Driven Approach (with queued jobs per record)

If you want to avoid polling, you could, upon saving a future published_at, dispatch a delayed job for that record:

// When you set published_at in the future:

$delay = Carbon::parse($post->published_at)->diffInSeconds(now());

CheckPublishedJob::dispatch($post)->delay($delay);

But this has downsides:

  • Not robust if your queue system restarts or jobs are cleared.
  • Better for low volume.

Takeaway

For most cases, a scheduled job is the cleanest, most robust Laravel solution for this scenario.


Recap:
You need to use a scheduled command to check for records whose published_at date has arrived, and process them accordingly. Observers can't "see" that time-based transition, as no database update is happening at that moment.

Let me know if you need more implementation details!

vincent15000's avatar

An observer will just listen to events. If you don't trigger any event, the observer doesn't do anything. Except for all CRUD actions (creating, created, updating, updated, ...) for which Laravel fires events automatically.

In your case, I think that you don't need any observer, but just a custom artisan command and execute it periodically via a cron task.

But I don't know exactly what you are trying to do, so it's difficult to give you the right answer without knowing more about the context.

Glukinho's avatar

You misunderstand what observer is. It has nothing to do with your task. You should schedule a command every N minutes (whatever frequency you prefer) which:

  • takes records ready to be published (in other words, which have published_at in less than N minutes in future)
  • publishes them
  • applies service function you want to be applied to "scheduled" records.

Model observers are about events system, this is another topic.

Please or to participate in this conversation.