PHPackages                             laragear/rewind - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. laragear/rewind

ActiveLibrary[Database &amp; ORM](/categories/database)

laragear/rewind
===============

Travel back in time to see past model states.

v3.0.0(2mo ago)1217MITPHPPHP ^8.3CI passing

Since Jul 30Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/Laragear/Rewind)[ Packagist](https://packagist.org/packages/laragear/rewind)[ Fund](https://github.com/sponsors/DarkGhostHunter)[ Fund](https://paypal.me/darkghosthunter)[ RSS](/packages/laragear-rewind/feed)WikiDiscussions 3.x Synced 1mo ago

READMEChangelog (4)Dependencies (8)Versions (7)Used By (0)

Rewind
======

[](#rewind)

[![Latest Version on Packagist](https://camo.githubusercontent.com/a2615bc2ace53f15a078a56da602eaf3f313e77ef1eb64b1f39f27f5cb1e8548/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6c617261676561722f726577696e642e737667)](https://packagist.org/packages/laragear/webauthn)[![Latest stable test run](https://github.com/Laragear/Rewind/actions/workflows/php.yml/badge.svg?branch=1.x)](https://github.com/Laragear/Rewind/actions/workflows/php.yml)[![Codecov coverage](https://camo.githubusercontent.com/39d965f26f5c0dd1805574decaca795897f9de6ab7087e8e8fc252da88a1ebcc/68747470733a2f2f636f6465636f762e696f2f67682f4c617261676561722f526577696e642f67726170682f62616467652e7376673f746f6b656e3d6b675a704b4c4b52316a)](https://codecov.io/gh/Laragear/Rewind)[![Maintainability](https://camo.githubusercontent.com/7e4d55ee22bc0f2e6d0b0bd495b19cf1d03bef03f53724b41db5f4ff783df421/68747470733a2f2f716c74792e73682f6261646765732f34316561393238302d353131352d343531352d386332332d6333633137343531653365302f6d61696e7461696e6162696c6974792e737667)](https://qlty.sh/gh/Laragear/projects/Rewind)[![Sonarcloud Status](https://camo.githubusercontent.com/3eec03b3233101342e568c3e82c39b6218b1aa3818fd2e66ce9593d168ec70ae/68747470733a2f2f736f6e6172636c6f75642e696f2f6170692f70726f6a6563745f6261646765732f6d6561737572653f70726f6a6563743d4c617261676561725f526577696e64266d65747269633d616c6572745f737461747573)](https://sonarcloud.io/dashboard?id=Laragear_Rewind)[![Laravel Octane Compatibility](https://camo.githubusercontent.com/70359a356da237cd29561bc5d0bb80baae775b5ff62f288ed324755382858342/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2532304f6374616e652d436f6d70617469626c652d737563636573733f7374796c653d666c6174266c6f676f3d6c61726176656c)](https://laravel.com/docs/13.x/octane#introduction)

Travel back in time to see past model states and restore them in one line.

```
use App\Models\Article;

$article = Article::find(1);

$article->rewind()->toLatest();
```

Become a sponsor
----------------

[](#become-a-sponsor)

[![](.github/assets/support.png)](https://github.com/sponsors/DarkGhostHunter)

Your support allows me to keep this package free, up-to-date and maintainable. Alternatively, you can **spread the word on social media**!

Requirements
------------

[](#requirements)

- PHP 8.3 or later
- Laravel 12 or later

Installation
------------

[](#installation)

Call Composer to retrieve the package.

```
composer require laragear/rewind
```

Setup
-----

[](#setup)

First, install the migration file. This migration will create a table to save all previous states from your models.

```
php artisan vendor:publish --provider="Laragear\Rewind\RewindServiceProvider" --tag="migrations"
```

Tip

You can [edit the migration](DATABASE.md#migration-customization) by adding new columns before migrating, and also [change the table name](DATABASE.md#model-customization).

```
php artisan migrate
```

Finally, add the `HasRewind` trait into your models you want its state to be saved when created and updated.

```
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Laragear\Rewind\HasRewind;

class Article extends Model
{
    use HasRewind;

    // ...
}
```

That's it. Next time you want restore the previous state of a model, use the `rewind()` method and one of the helper methods, like `toLatest()`.

```
use App\Models\Article;

$article = Article::find(1);

$article->rewind()->toLatest();
```

How it works?
-------------

[](#how-it-works)

When your model is created, and subsequently updated, a new *state* is saved into the database. That state in comprised of the raw attributes of your original model.

With the `rewind()` helper method, you can easily peek and restore a previous model states like the last one, or have a list of states ready to be restored from. Additionally, the `HasRewind` trait allows to control *when* to save states, and *what* to save and restore.

States are *reactive*, not *proactive*. In other words, states are saved *after* the original model is saved, not *before*.

Saving States
-------------

[](#saving-states)

States are created automatically when the model is created or updated. There is nothing you need to do to ensure the state has been persisted, but you can [listen for the `StatePushed` event](#events).

```
use App\Models\Article;
use Illuminate\Http\Request;

public function store(Request $request)
{
    $validated = $request->validate([
        // ...
    ]);

    return Article::create($validated); // First state created automatically.
}

public function update(Article $article, Request $request)
{
    $validated = $request->validate([
        // ...
    ]);

    $article->update($validated); // State pushed automatically.

    return $article;
}
```

### Without creating states

[](#without-creating-states)

Sometimes you will want to avoid creating a replica when a model is created or updated.

You may use the `irreversible()` to disable rewinds, and `reversible()` to enable it again.

```
use App\Models\Article;

Article::irreversible();

$article = Article::create($validated)

Article::reversible();
```

Alternatively, you may use the `whileIrreversible()` method of your model with a callback. Inside the callback, rewinding will be disabled so states won't be pushed to the database.

```
use App\Models\Article;
use Illuminate\Http\Request;

public function store(Request $request)
{
    $validated = $request->validate([
        // ...
    ]);

    // Create an article but don't save the first state.
    return Article::whileIrreversible(fn() => Article::create($validated));
}
```

### Manually creating States

[](#manually-creating-states)

If you have disabled [automatic states creation](#automatic-state-saving), then you may save a state manually using the `create()` method.

```
use App\Models\Article;
use Illuminate\Http\Request;

public function store(Request $request)
{
    $validated = $request->validate([
        // ...
    ]);

    $article = Article::create($validated);

    $article->rewind()->create(); // Save the first state.

    return $article;
}

public function update(Article $article, Request $request)
{
    $validated = $request->validate([
        // ...
    ]);

    $article->update($validated);

    $article->rewind()->create(); // Push a new state.

    return $article;
}
```

The `create()` method allows [keep the state](#keep-state) from automatic pruning, and also skip automatic pruning.

```
$article->rewind()->create(
    keep: true,
    prune: false,
);
```

If you want, you can save a state without updating the original model. For example, you can create a bunch of articles "drafts".

```
public function draft(Article $article, Request $request)
{
    $validated = $request->validate([
        // ...
    ]);

    // Push a state from the updated article without persisting it.
    $article->fill($validated)->rewind()->create();

    return back();
}
```

Listing States
--------------

[](#listing-states)

To get a list of all prior models states use the `all()` method. It will return an [Eloquent Collection](https://laravel.com/docs/11.x/eloquent-collections#available-methods) of all past models.

```
use App\Models\Article;

$pastArticles = Article::find(1)->rewind()->all();
```

Tip

You can use the state ID to later [restore a given state ID](#restoring-states).

### Count

[](#count)

To count all the saved states, use the `count()` method. It will return the number of persisted states.

```
use App\Models\Article;

$count = Article::find(1)->rewind()->count();
```

### Existence

[](#existence)

To avoid counting all the states and only check if there is at least one state made for the model, use the `exists()` method.

```
use App\Models\Article;

$hasAtLeastOneState = Article::find(1)->rewind()->exists();
```

Alternatively, the `missing()` method checks if there are no states saved for the model.

```
use App\Models\Article;

$hasNoStates = Article::find(1)->rewind()->missing();
```

### Latest and Oldest states

[](#latest-and-oldest-states)

You may also use `findLatest()` and `findOldest()` if you need to find the first or the last model state, respectively.

```
use App\Models\Article;

$latest = Article::find(1)->rewind()->getLatest();

$oldest = Article::find(1)->rewind()->getOldest();
```

### Retrieving a State ID

[](#retrieving-a-state-id)

To retrieve a model instance by its state ID, use the `find()` method.

```
use App\Models\Article;

$pastArticle = Article::find(1)->rewind()->find(456);
```

Caution

Because the State ID is expected to exist, a `ModelNotFoundException` will be thrown if id doesn't exist.

Restoring States
----------------

[](#restoring-states)

The easiest way to restore a prior state data into the same model instance is using the `to()` method with the ID of the state to restore, and just calling `save()` to persist the changes in the database.

```
use App\Models\Article;

$article = Article::find(1);

$article->title; // "Happy cavations!"

$article->rewind()->to(468)->save();

$article->title; // "Happy vacations!"
```

Alternatively, you can always restore the model to the latest or oldest state using `toLatest()` or `toOldest()`, respectively.

```
use App\Models\Article;

Article::find(1)->rewind()->toLatest()->save();

Article::find(1)->rewind()->toOldest()->save();
```

Important

When the model restored is updated, it will create a new state. To avoid this, use [`withoutCreatingStates()`](#without-creating-states).

### Restoring states alongside the original model

[](#restoring-states-alongside-the-original-model)

If you retrieve prior model state, you will virtually have two instances in your code: the current one, and the past state model.

Saving the past state will replace the data of the original in the database. The original instance will not be aware of the changes made, so you should [refresh the model](https://laravel.com/docs/11.x/eloquent#refreshing-models), or discard it.

```
use App\Models\Article;

$stale = Article::find(1);

$stale->title; // "Happy vatacions!"

$past = $original->rewind()->getLatest();

$past->save();

$stale->title; // "Happy vatacions!"

$stale->fresh()->title; // "Happy vacations!"
```

Deleting states
---------------

[](#deleting-states)

You may delete a state the `delete()` and the ID of the state.

```
use App\Models\Article;

Article::find(1)->rewind()->remove(765);
```

You may also use the `deleteLatest()` or `deleteOldest()` to delete the latest or oldest state, respectively.

```
use App\Models\Article;

$article = Article::find(1);

$article->rewind()->removeLatest();
$article->rewind()->removeOldest();
```

Important

Using `deleteLatest()` and `deleteOldest()` do not delete [kept states](#keep-state), you will need to issue `true` to force its deletion.

```
Article::find(3)->rewind()->deleteOldest(true);
```

### Deleting all states

[](#deleting-all-states)

You may call the `clear()` method to delete all the states from a model.

```
use App\Models\Article;

Article::find(1)->rewind()->clear();
```

Since this won't include [kept states](#keep-state), you can use `forceClear()` to include them.

```
use App\Models\Article;

Article::find(1)->rewind()->forceClear();
```

### Pruning states

[](#pruning-states)

Every time the model is updated, it automatically prunes old model states to keep a [limited number of states](#limiting-number-of-saved-states). If you have disabled it, you may need to call the `prune()` method manually to remove stale states.

```
use App\Models\Article;

Article::find(1)->rewind()->prune();
```

Note

When retrieving states, states to-be-pruned are automatically left out from the query.

Events
------

[](#events)

This package fires the following events:

Event nameTrigger[StateRestored](src/Events/StateRestored.php)At developer discretion[StateDeleted](src/Events/StateDeleted.php)Fires when a state is deleted from the database[StatePushed](src/Events/StateCreated.php)Fires when a state is pushed to the database[StatesCleared](src/Events/StatesCleared.php)Fires when states are cleared from the database[StatesPruned](src/Events/StatesPruned.php)Fires when states are pruned from the databaseFor example, you can listen to the `StatePushed` event in your application with a [Listener](https://laravel.com/docs/11.x/events#registering-events-and-listeners).

```
use Laragear\Rewind\Events\StateCreated;

class MyListener
{
    public function handle(StateCreated $event)
    {
        $event->model; // The target model.
        $event->state; // The RewindState Model with the data.
    }
}
```

The `StateRestored` event is included for convenience. If you decide to restore a model to a previous state, you may trigger it using `StateRestored::dispatch()` manually with the model instance.

```
use App\Models\Article;
use Illuminate\Support\Facades\Route;
use Laragear\Rewind\Events\StateRestored;

Route::post('article/{article}/restore', function (Article $article) {
    $article->rewind()->toOldest()->save();

    StateRestored::dispatch($article);

    return back();
});
```

Raw States
----------

[](#raw-states)

Model States data are saved into the `Laragear\Rewind\Models\RewindState` model. This model is mostly responsable for creating a new model instance from the raw data it holds.

### Querying Raw States

[](#querying-raw-states)

Use the `query()` method to start a query for the given model. You may use this, for example, to paginate results.

```
use App\Models\Article;

$page = Article::find(1)->rewind()->query()->paginate();
```

You may also use the model instance query directly for further custom queries.

```
use Laragear\Rewind\Models\RewindState;

RewindState::query()->where('created_at', 'subMonth();
}
```

To limit by both an amount and a date at the same time, return an array with both.

```
public function rewindLimit()
{
    return [10, now()->subMonth()];
}
```

Finally, you may disable limits by returning *falsy*, which will ensure all states are saved.

```
public function rewindLimit()
{
    return false;
}
```

### Automatic State saving

[](#automatic-state-saving)

Every time you create or update a model, a new state is created in the database. You may change this behaviour using `shouldCreateRewindStateOnCreated()` and `shouldCreateRewindStateOnUpdated()`, respectively.

```
public function shouldCreateRewindStateOnCreated(): bool
{
    // Don't save states if the article body is below a certain threshold.
    return strlen($this->body) < 500;
}

public function shouldCreateRewindStateOnUpdated(): bool
{
    // Only let subscribed users to have states saved.
    return $this->user->hasSubscription();
}
```

This may be useful if you want to not save a model state, for example, if an Article doesn't have enough characters in its body, or if the User is not [subscribed to a plan](https://github.com/Laragear/SubscriptionsDemo).

### Automatic pruning

[](#automatic-pruning)

By default, everytime a new state is pushed into the database, it prunes the old states. You may disable this programmatically using `shouldPruneOldRewindStatesOnUpdated()`.

```
public function shouldPruneOldRewindStatesOnUpdated(): bool
{
    return true;
}
```

### Keep State

[](#keep-state)

When you create a model, the initial state can be lost after pushing subsequent new states. To avoid this, you can always *protect* the first state, or any state, with `shouldKeepFirstRewindState()`.

```
public function shouldKeepFirstRewindState(): bool
{
    return true;
}
```

Tip

If the first state is protected, it won't be pruned, so only newer states will rotate.

### Attributes to save in a State

[](#attributes-to-save-in-a-state)

By default, all raw attributes of the model are added into the state data. You can override this by setting your own attributes to use in the state to save by returning an associative array or an `\Illuminate\Contracts\Support\Arrayable` instance.

```
use Illuminate\Contracts\Support\Arrayable;

public function getAttributesForRewindState(): Arrayable|array
{
    return [
        'author_id' => $this->author_id,
        'title' => $this->title,
        'slug' => $this->slug,
        'body' => $this->body,
        'private_notes' => null,
        'published_at' => $this->published_at,
     ];
}
```

### Attributes to restore from a State

[](#attributes-to-restore-from-a-state)

To modify *how* the model should be filled with the attributes from the state, use the `setAttributesFromRewindState()`. It receives the raw attributes from the state as an array.

```
use Illuminate\Support\Arr;

public function setAttributesFromRewindState(array $attributes): void
{
    $this->setRawAttributes(Arr::except($attributes, 'excerpt'));
}
```

[Migrations](DATABASE.md)
-------------------------

[](#migrations)

Laravel Octane compatibility
----------------------------

[](#laravel-octane-compatibility)

- There are no singletons using a stale application instance.
- There are no singletons using a stale config instance.
- There are no singletons using a stale request instance.
- There are no static properties written on every request.

There should be no problems using this package with Laravel Octane.

Security
--------

[](#security)

If you discover any security-related issues, please email  instead of using the issue tracker.

License
=======

[](#license)

This specific package version is licensed under the terms of the [MIT License](LICENSE.md), at the time of publishing.

[Laravel](https://laravel.com) is a Trademark of [Taylor Otwell](https://github.com/TaylorOtwell/). Copyright © 2011–2026 Laravel LLC.

###  Health Score

44

—

FairBetter than 92% of packages

Maintenance87

Actively maintained with recent releases

Popularity13

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity59

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~98 days

Total

7

Last Release

62d ago

Major Versions

v1.0.1 → 2.x-dev2025-02-18

v2.0.0 → 3.x-dev2026-03-08

PHP version history (3 changes)v1.0.0PHP ^8.1

2.x-devPHP ^8.2

3.x-devPHP ^8.3

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/5141911?v=4)[Italo](/maintainers/DarkGhostHunter)[@DarkGhostHunter](https://github.com/DarkGhostHunter)

---

Top Contributors

[![DarkGhostHunter](https://avatars.githubusercontent.com/u/5141911?v=4)](https://github.com/DarkGhostHunter "DarkGhostHunter (7 commits)")

---

Tags

laravelhistorystatesrewindtimemachine

### Embed Badge

![Health badge](/badges/laragear-rewind/health.svg)

```
[![Health](https://phpackages.com/badges/laragear-rewind/health.svg)](https://phpackages.com/packages/laragear-rewind)
```

###  Alternatives

[tucker-eric/eloquentfilter

An Eloquent way to filter Eloquent Models

1.8k4.8M26](/packages/tucker-eric-eloquentfilter)[watson/validating

Eloquent model validating trait.

9723.3M46](/packages/watson-validating)[yajra/laravel-oci8

Oracle DB driver for Laravel via OCI8

8703.0M17](/packages/yajra-laravel-oci8)[cybercog/laravel-love

Make Laravel Eloquent models reactable with any type of emotions in a minutes!

1.2k302.7k1](/packages/cybercog-laravel-love)[cviebrock/eloquent-taggable

Easy ability to tag your Eloquent models in Laravel.

567694.8k3](/packages/cviebrock-eloquent-taggable)[clickbar/laravel-magellan

This package provides functionality for working with the postgis extension in Laravel.

423715.4k1](/packages/clickbar-laravel-magellan)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
