Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Pending Updates Laravel Package

stfn/laravel-pending-updates

A lightweight Laravel package for tracking pending updates. Queue changes to your models, inspect what is waiting to be applied, and flush them when ready. Useful for batching writes, deferring side effects, and keeping data in sync without blocking requests.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish

composer require stfn/laravel-pending-updates
php artisan vendor:publish --provider="Stfndamjanovic\PendingUpdates\PendingUpdatesServiceProvider"
php artisan migrate

The package ships a service provider, a config file, and at least one migration (typically a pending_updates-style table that stores model reference, attribute payload, and an effective/expiry timestamp). Publish the config and migration together — the config usually contains the table name, the queue/connection used for any deferred work, and the date/time semantics.

Where to look first

  1. README.md usage section — confirm the exact trait name and method signatures before writing anything. At 22 stars and a single 2023 release, the docs are the only reliable contract; don't guess method names from the description.
  2. config/pending-updates.php — the table name, the scheduling behaviour, and any "expiry" defaults live here. Change these before you write migrations in your app.
  3. The published migration — check the column types (especially json vs text for the payload) and whether it indexes the polymorphic (model_type, model_id) pair. If it doesn't, add an index in a follow-up migration.

First use case

The most common day-one need is a scheduled attribute change: a price, status, or visibility flag that should flip at a specific time without a deploy or a manual admin click.

use Stfndamjanovic\PendingUpdates\HasPendingUpdates;

class Product extends Model
{
    use HasPendingUpdates;
}

// Later, anywhere in your app:
$product->postponeUpdate(['price' => 2999], until: now()->addDays(3));

The second use case is the inverse — temporary overrides that revert on their own (a maintenance banner, a promotional title, a feature flag on a single record). Reach for the package when you catch yourself writing if (now()->between($start, $end)) inside an accessor.


Implementation Patterns

Pattern 1: Time-boxed attribute overrides

Instead of scattering date-range checks through accessors and Blade templates, store the override once and let a scheduled command apply and revert it:

$product->updateTemporarily(['title' => 'Black Friday Deal'], for: '3 days');

The win is that reads stay dumb — your views, API resources, and search index see one consistent row. If the package only exposes the pending payload and not an applied value, wrap the read in a small accessor that merges the pending row over the base attribute; keep that wrapper in one place (a trait method or an Eloquent scope) so you can swap it out later.

Pattern 2: Scheduled application via the console kernel

Whatever the package gives you, drive it from the scheduler rather than from request-time code:

// app/Console/Kernel.php
$schedule->command('pending-updates:apply')->everyMinute()->withoutOverlapping();

withoutOverlapping() is not optional. A minute-by-minute job that writes to models will occasionally run long, and two concurrent passes will double-apply idempotent-to-the-row but not idempotent-to-events updates.

Pattern 3: Pair with model events deliberately

Applying a pending update through Eloquent fires updating/updated and therefore your observers, audit logs, and broadcast events. That's usually what you want (a price change should notify watchers), but it also means a bulk apply fires N events in one job. For high-volume models, batch inside a transaction and dispatch a single aggregate event, or use saveQuietly()-style application if the package supports it.

Pattern 4: Admin UI as a thin layer over the payload

The natural integration point is a form that writes a pending update rather than the model directly. Store the actor (created_by) alongside the payload in your own column if the package doesn't — "who scheduled this price change" is the first question you'll be asked when it goes wrong.

Pattern 5: Testing with time travel

$this->travelTo(now()->addDays(3));
$this->artisan('pending-updates:apply')->assertExitCode(0);
$this->assertEquals(2999, $product->fresh()->price);

Because the whole package is time-dependent, every test should pin the clock explicitly. Never rely on now() in the assertion.


Gotchas and Tips

The package is small and quiet — budget for reading the source. One release (March 2023), 22 stars, zero dependents. That's not a red flag, but it means you are the maintainer of last resort. Read the ~few hundred lines of source before adopting; if anything critical is missing (indexes, idempotency, transaction wrapping), fork it into app/Support rather than opening a PR you'll wait months on.

Pin the version. composer require stfn/laravel-pending-updates:^1.0 with a committed composer.lock. An unpinned dependency on a low-traffic package is how you get a surprise Laravel-major incompatibility on a Friday.

Timezone drift is the #1 bug class here. Postponed updates compare a stored timestamp against now(). If your DB stores UTC but the config expects app timezone (or vice versa), updates fire hours early or late — and only in production, because your test suite runs in a single timezone. Standardise on UTC in the column, convert in the config/UI layer, and write one test that asserts a specific offset boundary.

In-memory models go stale. A model loaded before the apply command ran will still hold old attributes. Anything long-lived — queue workers, Octane workers, cached view models — must fresh() or be invalidated. If you're on Octane, add the apply command's effects to your cache-busting story.

Cache and index invalidation aren't automatic. If you cache rendered product pages or push to Meilisearch/Elasticsearch, applying a pending update changes the DB but not your derived stores. Hook the apply step (observer or command-finished event) into your existing invalidation path; the package won't know about them.

Migrations ordering matters. The published migration creates the polymorphic table; if you add your own columns (created_by, applied_at, reason), do it in a separate, later migration. Editing the published one means republishing on upgrade will silently overwrite your changes.

Mass assignment and casts. The payload is serialised, so casts on the parent model (price as decimal:2, meta as array) must be re-applied on the way out. If the package stores raw DB values, remember you're handing it raw values, not cast values — a Carbon passed into the payload may round-trip as a string. Test the round trip for any cast attribute you postpone.

Soft deletes and restore(). A pending update attached to a soft-deleted model should either be skipped or cascade-deleted. Verify which, and add a deleted guard in your apply loop if the package doesn't — otherwise the first forceDelete() leaves orphans that the polymorphic lookup will keep trying to resolve.

Extension points worth knowing. Model events on apply (for logging/notifications), the scheduler command's exit code (for alerting), and the config's table name (for multi-tenant or sharded setups where you route the table to a different connection). If any of these are missing, a thin wrapper class in your app — one method that applies and one that schedules — gives you a seam to replace the package later without touching call sites.

Debounce before you postpone. Two admins scheduling the same price change five seconds apart creates two pending rows. Add a uniqueness constraint on (model_type, model_id, attribute) or an "upsert" helper; the package almost certainly doesn't dedupe for you.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator