PHPackages                             plank/laravel-checkpoint - 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. plank/laravel-checkpoint

ActiveLibrary

plank/laravel-checkpoint
========================

A package for keeping a history of your models' revisions and accessing your data as it was at an older date.

v2.1.0(4y ago)810.3k3[3 issues](https://github.com/plank/laravel-checkpoint/issues)[2 PRs](https://github.com/plank/laravel-checkpoint/pulls)MITPHPPHP ^7.1.3|^8.0CI failing

Since Aug 4Pushed 4y ago10 watchersCompare

[ Source](https://github.com/plank/laravel-checkpoint)[ Packagist](https://packagist.org/packages/plank/laravel-checkpoint)[ Docs](https://github.com/plank/laravel-checkpoint)[ RSS](/packages/plank-laravel-checkpoint/feed)WikiDiscussions master Synced today

READMEChangelog (5)Dependencies (5)Versions (30)Used By (0)

Laravel Checkpoint
==================

[](#laravel-checkpoint)

[![Latest Version on Packagist](https://camo.githubusercontent.com/933fdc3f381bcae8480a9d2156369485f1397e4314b98c754cd132d2fd026806/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f706c616e6b2f6c61726176656c2d636865636b706f696e742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/plank/laravel-checkpoint)[![GitHub Tests Action Status](https://github.com/plank/laravel-checkpoint/actions/workflows/tests.yml/badge.svg)](https://github.com/plank/laravel-checkpoint/actions?query=workflow%3Atests+branch%3Amaster)[![Total Downloads](https://camo.githubusercontent.com/ccad96bde7aeea9f54819113b7d6e4d97c708037de42c2dff7ffeacd84ec6336/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f706c616e6b2f6c61726176656c2d636865636b706f696e742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/plank/laravel-checkpoint)

Table of Contents
-----------------

[](#table-of-contents)

- [Laravel Checkpoint](#laravel-checkpoint)
    - [Table of Contents](#table-of-contents)
    - [Why Use This Package](#why-use-this-package)
    - [Installation](#installation)
    - [Concepts](#concepts)
        - [Timelines](#timelines)
        - [Checkpoints](#checkpoints)
        - [Revisions](#revisions)
    - [Usage](#usage)
        - [Revisioning Models](#revisioning-models)
            - [What gets Revisioned?](#what-gets-revisioned)
            - [Start Revisioning Command](#start-revisioning-command)
        - [Query Scopes](#query-scopes)
            - [Active Checkpoint](#active-checkpoint)
            - [at($moment)](#atmoment)
            - [since($moment)](#sincemoment)
            - [temporal($upper, $lower)](#temporalupper-lower)
            - [withoutRevisions()](#withoutrevisions)
        - [Revision Metadata &amp; Uniqueness](#revision-metadata--uniqueness)
        - [Unwatched Fields](#unwatched-fields)
        - [Should Revision](#should-revision)
        - [Excluded Columns](#excluded-columns)
        - [Excluded Relations](#excluded-relations)
    - [Testing](#testing)
    - [Changelog](#changelog)
    - [Contributing](#contributing)
    - [Security](#security)
    - [Credits](#credits)
    - [License](#license)

Why Use This Package
--------------------

[](#why-use-this-package)

Do you need to store the state of how your models change over time? Do you need a way to query and view the state of your models at different points in time? If the answer is yes, then this package is for you!

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

[](#installation)

You can install the package via composer:

```
composer require plank/laravel-checkpoint
```

Concepts
--------

[](#concepts)

### Timelines

[](#timelines)

A `Timeline` is a way to have completely separate views of your content. A `Timeline` allows you to filter the `Revision`s of your models based on the `Timeline` it belongs to.

Table: `timelines`

FieldTypeRequiredDefaultidbigIncrements✗Incrementtimeline\_idunsignedBigInteger✗titlestring✓checkpoint\_datetimestamp✓created\_attimestamp✗updated\_attimestamp✗### Checkpoints

[](#checkpoints)

A `Checkpoint` is a point in time which is of interest. A `Checkpoint` allows you to filter the `Revision`s of your models based on the `Checkpoint`'s `checkpoint_date`.

Table: `checkpoints`

FieldTypeRequiredDefaultidbigIncrements✗Incrementtimeline\_idunsignedBigInteger✗titlestring✓checkpoint\_datetimestamp✓created\_attimestamp✗updated\_attimestamp✗### Revisions

[](#revisions)

A `Revision` references a record of a `Model` in a particular state at a particular point in time. When this package is enabled, and you use the `HasRevisions` trait on a *Model*, the concept of an instance of a *Model* in Laravel changes. Since we want to store `Revision`s of a *Model*, and have them searchable in their different states, the notion that an *Entity* (instance of a *Model*) is associated with exactly one id, is no longer correct. Each `Revision` of a *Model* has its own unique id in the table, even though it represents the same *Entity*.

The same entity is linked via the `original_revisionable_id` field.

Table: `revisions`

FieldTypeRequiredDefaultidbigIncrements✗Incrementrevisionable\_idunsignedBigInteger✓revisionable\_typestring✓original\_revisionable\_idunsignedBigInteger✓latestboolean✗truemetadatajson✗nullprevious\_revision\_idunsignedBigInteger✗nullcheckpoint\_idunsignedBigInteger✗nullcreated\_attimestamp✗updated\_attimestamp✗Usage
-----

[](#usage)

### Revisioning Models

[](#revisioning-models)

To have a model be revisioned, all you need to do is have it use the `HasRevisions` trait.

#### What gets Revisioned?

[](#what-gets-revisioned)

This package handles revisioning by creating a new row for a *Model* in the database every time it changes state in a meaningful way. When a new `Revision` is created, the package will also recursively duplicate all *Models* related via child relationships, and will create new many-to-many relationships in pivot tables.

#### Start Revisioning Command

[](#start-revisioning-command)

If you have an existing project with *Models* already populated in the database, the `php artisan checkpoint:start`command will begin revisioning all of the *Models* which are using the `HasRevsions` trait.

### Query Scopes

[](#query-scopes)

The way this package achieves it's goal is by adding scopes (and one global scope) to query models that have revisions.

#### Active Checkpoint

[](#active-checkpoint)

By setting the active checkpoint `Checkpoint::setActive($checkpoint)`, all queries for revisioned models will be scoped to that `$checkpoint`. Also, when there is an active checkpoint set, any new revisions that get created will be associated with that `$checkpoint`.

#### at($moment)

[](#atmoment)

```
/**
 * @param $moment Checkpoint|Carbon|string
 */
at($moment = null)
```

This is the default global query scope added to all queries on a *Model* with `Revision`s.

This query scope will limit the query to return the *Model* whose `Revision` has the max primary key, where the `Revision` was created at or before the given moment.

The moment can either be an instance of a `Checkpoint`using its `checkpoint_date` field, a string representation of a date or a `Carbon` instance.

#### since($moment)

[](#sincemoment)

```
/**
 * @param $moment Checkpoint|Carbon|string
 */
since($moment = null)
```

This query scope will limit the query to return the *Model* whose `Revision` has the max primary key, where the `Revision` was created after the given moment.

The moment can either be an instance of a `Checkpoint` using its `checkpoint_date` field, a string representation of a date or a `Carbon` instance.

#### temporal($upper, $lower)

[](#temporalupper-lower)

```
/**
 * @param $upper Checkpoint|Carbon|string
 * @param $upper Checkpoint|Carbon|string
 */
temporal($until = null, $since = null)
```

This query scope will limit the query to return the *Model* whose `Revision` has the max primary key created at or before `$until`. This method can also limit the query to the *Model* whose revision has the max primary key created after `$since`.

Each argument operates independently of each other and `$until` and `$since` can either be an instance of a `Checkpoint` using its `checkpoint_date` field, a string representation of a date or a `Carbon` instance.

#### withoutRevisions()

[](#withoutrevisions)

```
withoutRevisions()
```

This query scope is used to query the models without taking revisioning into consideration.

### Dynamic Relationships

[](#dynamic-relationships)

Inspired by , this package supplies a few dynamic relationships as a convenience for navigating through a model's revision history. The following scopes will run subqueries to get the additional columns and eagerload the corresponding relations, saving you the hassle of caching them on each of the tables for your revisionable models. As a fallback when these scopes are not applied, we use get mutators to run queries and fetch the same columns, making sure the relations are always available but at the expense of running a bit more queries. *NOTE: when applying these scopes, you will have extra columns in your models attributes, **any update or insert operations will not work.***

#### withNewestAt($until, $since)

[](#withnewestatuntil-since)

```
/**
 * @param $until Checkpoint|Carbon|string
 * @param $since Checkpoint|Carbon|string
 */
withNewestAt($until = null, $since = null)
```

This scope will retrieve the id of the newest model given the until / since constraints. Stored in the newest\_id attribute, this allows you to use `->newest()` relation as a quick way to navigate to that model. Defaults to the newest model in the revision history.

#### withNewest()

[](#withnewest)

This scope is a shortcut of `withNewestAt` with the default parameters. Uses the same attribute, mutator and relation.

#### withInitial()

[](#withinitial)

This scope will retrieve the id of the initial model from its revision history. Stored in the initial\_id attribute, this allows you to use `->initial()` relation as a quick way to navigate to that first item in the revision history.

#### withPrevious()

[](#withprevious)

This scope will retrieve the id of the previous model from its revision history. Stored in the previous\_id attribute, this allows you to use `->previous()` relation as a quick way to navigate to that previous item in the revision history.

#### withNext()

[](#withnext)

This scope will retrieve the id of the next model from its revision history. Stored in the next\_id attribute, this allows you to use `->next()` relation as a quick way to navigate to that next item in the revision history.

### Revision Metadata &amp; Uniqueness

[](#revision-metadata--uniqueness)

As a workaround to some package compatibility issues, this package offers a convenient way to store the values of some columns as `metadata` on the `revisions` table. The primary use-case for this feature is to deal with columns or indexes which force some sort of uniqueness constraint on the *Model's* table.

For example, imagine a `Room` model we wish to revision and it has a `code` field which needs to be unique. Since multiple instances of the same `Room` need to exist as revisions, there would be duplicated `codes`. By specifying the `code` field in the`protected $revisionMeta;` of the `Room` *Model*, this package will manage this field by storing it as metadata on the `Revision`. The package achieve's this by overriding the `getAttributeValue($value)` method on the model, to retrieve the value of `code` from the `Revision`. When saving a new `Revision` of the `Room` the `code` will automatically be saved on the `metadata` field of the revision and set as null on the `Room`.

### Ignored Fields

[](#ignored-fields)

When updating the fields of a *Model*, some fields may not warrant creating a new `Revision` of the *Model*. You can prevent a new `Revision` from being created when specific fields are updated by setting the `protected $ignored`array on the model being revisioned.

### Should Revision

[](#should-revision)

If you have more complex cases where you may not want to create a new `Revision` when updating a *Model*, you can override the `public function shouldRevision()` on the *Model* being revisioned. When this method returns a truthy value, a new `Revision` will be created when updating, and when it returns a falsy value it will not.

### Excluded Columns

[](#excluded-columns)

When creating a new `Revision` of a *Model* there may be some fields which do not make sense to have their values copied over. In those cases you can add those values to the ` protected $excluded` array on the *Model* you are revisioning. Some operations like deleting / restoring / revisioning children require a full copy and will ignore this option.

### Excluded Relations

[](#excluded-relations)

When creating a new `Revision` of a *Model* there may be relations which do not make sense to duplicate. In those cases you can add the names of the relations to the` protected $excludedRelations` array on the *Model* you are revisioning. Excluding all relations to the `Checkpoint`s and other related `Revision`s are handled by the package.

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details.

Security
--------

[](#security)

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

Credits
-------

[](#credits)

- [Massimo Triassi](https://github.com/m-triassi)
- [Andrew Hanichkovsky](https://github.com/WindOfRussia)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

35

—

LowBetter than 80% of packages

Maintenance13

Infrequent updates — may be unmaintained

Popularity26

Limited adoption so far

Community18

Small or concentrated contributor base

Maturity70

Established project with proven stability

 Bus Factor1

Top contributor holds 57% 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 ~35 days

Recently: every ~48 days

Total

18

Last Release

1510d ago

Major Versions

v0.2-beta → v1.0.02020-11-24

v1.0.0 → v2.0.0-alpha2021-09-09

v1.1.0 → 2.0.02021-11-10

PHP version history (2 changes)v0.0.1-alphaPHP ^7.1.3

v1.1.0PHP ^7.1.3|^8.0

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/108450?v=4)[Plank](/maintainers/Plank)[@plank](https://github.com/plank)

![](https://www.gravatar.com/avatar/88393469f450ec2f34cf4770ec5a889f65786808395e88b8401dd2cf79be8274?d=identicon)[&amp;drew](/maintainers/&drew)

![](https://www.gravatar.com/avatar/0c26e1e985ebb95a28a6eb8858d1cde4e63aef289306643453219b4fc11ab6e4?d=identicon)[m-triassi](/maintainers/m-triassi)

---

Top Contributors

[![a-drew](https://avatars.githubusercontent.com/u/6550234?v=4)](https://github.com/a-drew "a-drew (106 commits)")[![m-triassi](https://avatars.githubusercontent.com/u/9440691?v=4)](https://github.com/m-triassi "m-triassi (67 commits)")[![kfriars](https://avatars.githubusercontent.com/u/3378675?v=4)](https://github.com/kfriars "kfriars (13 commits)")

---

Tags

laravelpackagerevisiontraitrevisionversionversionablerevisionableplankcheckpoint

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/plank-laravel-checkpoint/health.svg)

```
[![Health](https://phpackages.com/badges/plank-laravel-checkpoint/health.svg)](https://phpackages.com/packages/plank-laravel-checkpoint)
```

###  Alternatives

[venturecraft/revisionable

Keep a revision history for your models without thinking, created as a package for use with Laravel

2.6k6.6M51](/packages/venturecraft-revisionable)[mpociot/versionable

Allows to create Laravel 4 / 5 / 6 / 7 / 8 / 9 / 10 / 11 Model versioning and restoring

7841.1M6](/packages/mpociot-versionable)[sofa/revisionable

Nice and easy way to handle revisions of your db.

258192.1k2](/packages/sofa-revisionable)[josegonzalez/cakephp-version

CakePHP ORM behavior to allow versioning of records

49145.2k](/packages/josegonzalez-cakephp-version)[proai/eloquent-versioning

An extension for the Eloquent ORM to support versioning.

7125.9k](/packages/proai-eloquent-versioning)[avto-dev/app-version-laravel

Laravel applications versioning

2549.4k2](/packages/avto-dev-app-version-laravel)

PHPackages © 2026

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