avocet-shores/laravel-rewind
Full version control for Eloquent models: rewind, fast-forward, restore, diff, and query point-in-time state. Uses hybrid diffs + snapshots for efficient storage and fast reconstruction, with locking for safe concurrent writes, batching, queues, and pruning.
Rewind::batch(fn) groups multiple model changes under a shared batch_uuid. Query with RewindVersion::inBatch($uuid).Rewind::restore($model, $version) creates a new version from a previous version's state. Tracks VersionEventType::Restored and stores restored_from_version in meta.version_model in config to extend RewindVersion with custom columns, scopes, or accessors.New batch_uuid column on the versions table. Publish and run:
php artisan vendor:publish --tag=laravel-rewind-migrations
php artisan migrate
// Custom version model (must extend RewindVersion)
'version_model' => null,
Closes #54, #55, #56.
Rewind::diff($model, $from, $to), returns a structured VersionDiff DTO with changed, added, and removed attributes.Rewind::withMeta(['reason' => '...']). Stored in a new meta JSON column. Metadata is automatically cleared after version creation.event_type (created, updated, or deleted) in a dedicated column, making version history queryable by action type.forModel(), byUser(), ofType(), betweenDates(), betweenVersions() for filtering version records.rewind:prune command with --keep, --days, --model, --pretend, and --force options. Automatically converts the new oldest version to a snapshot to preserve navigability.$maxRewindVersions property and global max_versions config. Pruning is batched using snapshot_interval as a buffer to amortize transaction costs.on_lock_timeout config with log, event, and throw modes. New RewindVersionLockTimeout event and LockTimeoutRewindException.(model_type, model_id, version) for faster version-scoped queries.deleted event to capture exact database timestamp.wasRecentlyCreated at dispatch time so they survive SerializesModels round-trips.current_version update, and auto-prune are now atomic.goTo() for concurrency safety.saveQuietly() when updating current_version to prevent unintended model events.StateBuilder service consolidating all state reconstruction logic.RewindManagerInterface contract for dependency injection.VersionPruner service, PruneResult DTO, RewindContext singleton, SchemaHelper utility.ApproachEngine now accepts Collection for use outside of model context.[@internal](https://github.com/internal) to define public API boundary.version field from composer.json (Packagist reads git tags).minimum-stability from dev to stable.CONTRIBUTING.md.Re-publish the config file to get all new options, or add them manually:
php artisan vendor:publish --tag="laravel-rewind-config" --force
| Key | Env Variable | Default | Description |
|---|---|---|---|
user_id_cast |
LARAVEL_REWIND_USER_ID_CAST |
integer |
Eloquent cast for the user ID column. Set to string for UUID primary keys. |
max_versions |
LARAVEL_REWIND_MAX_VERSIONS |
null |
Auto-prune old versions per model. null disables pruning. |
prune_keep_versions |
LARAVEL_REWIND_PRUNE_KEEP |
null |
Default --keep value for the rewind:prune command. |
prune_older_than_days |
LARAVEL_REWIND_PRUNE_DAYS |
null |
Default --days value for the rewind:prune command. |
on_lock_timeout |
LARAVEL_REWIND_ON_LOCK_TIMEOUT |
log |
Lock failure behavior: log, event, or throw. |
queue.tries |
LARAVEL_REWIND_QUEUE_TRIES |
3 |
Retry attempts for the queued listener. |
queue.timeout |
LARAVEL_REWIND_QUEUE_TIMEOUT |
60 |
Timeout (seconds) for the queued listener. |
queue.backoff |
-- | [2, 10, 30] |
Backoff delays (seconds) between retries. |
This release adds two new nullable columns to the rewind_versions table: event_type (string) and meta (json).
New installations get these columns automatically via the existing migration.
Existing installations should publish and run the new upgrade migration:
php artisan vendor:publish --tag="laravel-rewind-migrations"
php artisan migrate
The upgrade migration is idempotent — it checks for existing columns before adding them, so it's safe to run even if you've already added the columns manually.
Full Changelog: https://github.com/avocet-shores/laravel-rewind/compare/v0.7.4...v0.8.0
Full Changelog: https://github.com/avocet-shores/laravel-rewind/compare/v0.7.3...v0.7.4
Full Changelog: https://github.com/avocet-shores/laravel-rewind/compare/v0.7.2...v0.7.3
Full Changelog: https://github.com/avocet-shores/laravel-rewind/compare/v0.7.1...v0.7.2
Full Changelog: https://github.com/avocet-shores/laravel-rewind/compare/v0.7.0...v0.7.1
Full Changelog: https://github.com/avocet-shores/laravel-rewind/compare/v0.6.0...v0.7.0
$rewindStateFields on your model. Rewind records structured from/to transitions alongside each version. Query with whereStateBecame(), whereStateWas(), whereStateChanged(), and whereStateTransition() scopes, or get a full timeline with $model->stateHistory($field).New state_transitions column on the versions table. Publish and run:
php artisan vendor:publish --provider="AvocetShores\LaravelRewind\LaravelRewindServiceProvider"
php artisan migrate
How can I help you explore Laravel packages today?