eribloo / laravel-model-snapshots
Laravel package for storing model versions
Installs: 1 565
Dependents: 0
Suggesters: 0
Security: 0
Stars: 2
Watchers: 1
Forks: 1
Open Issues: 0
Requires
- php: ^8.1
- fidum/laravel-eloquent-morph-to-one: ^2.2
- illuminate/contracts: ^9.0|^10.0|^11.0
- spatie/laravel-package-tools: ^1.14.0
Requires (Dev)
- nunomaduro/collision: ^6.0
- nunomaduro/larastan: ^2.0.1
- orchestra/testbench: ^7.0
- pestphp/pest: ^1.21
- pestphp/pest-plugin-laravel: ^1.1
- phpstan/extension-installer: ^1.1
- phpstan/phpstan-deprecation-rules: ^1.0
- phpstan/phpstan-phpunit: ^1.0
- phpunit/phpunit: ^9.5
- roave/security-advisories: dev-latest
- tightenco/duster: ^1.0
README
⚠️ Disclaimer
This package is still under development and may change often.
Introduction
This package allows creating snapshots of models.
While a typical approach of adding a version column is often enough when there is a need of versioning models, this package stores snapshots in dedicated table. This provides better control over snapshotting process and keeps your tables clean.
My motivation while creating this package was to create configurable snapshots only when I need them, in contrast to generating new version with every update, while keeping connection to up-to-date original model.
Table of contents
Installation
You can install the package via composer:
composer require eribloo/laravel-model-snapshots
You can publish and run the migrations with:
php artisan vendor:publish --tag="model-snapshots-migrations"
php artisan migrate
You can publish the config file with:
php artisan vendor:publish --tag="model-snapshots-config"
This is the contents of the published config file:
return [ /** * Snapshot class used. Must implement EriBloo\LaravelModelSnapshots\Contracts\Snapshot interface. */ 'snapshot_class' => EriBloo\LaravelModelSnapshots\Models\Snapshot::class, /** * Versionist class used. Must implement EriBloo\LaravelModelSnapshots\Contracts\Versionist interface. */ 'versionist_class' => EriBloo\LaravelModelSnapshots\Support\Versionists\IncrementingVersionist::class, ];
Usage
Basics
Creating snapshots
You can create snapshot by using a helper snapshot()
function:
snapshot(Document::find(1))->commit();
This will snapshot model using default options defined in EriBloo\LaravelModelSnapshots\SnapshotOptions
class:
- set version with Versionist class defined in config
- snapshot all attributes, excluding primary key, timestamps and hidden
- it won't create snapshot if other snapshot with the same stored attributes already exists - in such situation matching snapshot will be returned
Each snapshot stores an array of model attributes, options that it was created with, version and optional description.
Snapshots provide toModel(bool $fillExcludedAttributes = false)
method, that returns model filled with
snapshotted attributes. If optional fillExcludedAttributes
option is true, returned model will use current model
attributes as a base, otherwise missing attributes will be null.
Accordingly, if you retrieve collection of snapshots you can use its toModels(bool $fillExcludedAttributes = false)
method to map all snapshots to corresponding classes.
Reverting, branching and forking snapshots
Snapshots have 3 helper methods to revert model, or to create a new one, from its snapshot:
revert()
- reverts original model to its snapshotted version, all snapshots created after the one used are deletedbranch()
- creates new model from snapshotted version and duplicates all snapshots up to, and including, the one used, associating them with new modelfork()
- creates new model from snapshotted version with no snapshots history
Attributes excluded from snapshotting will be filled with current model values.
Relations
In addition, package provides separate table to store snapshot relations with other models. There are morphToMany and
morphToOne relations available that return either Snapshots or Models in HasSnapshotRelations
trait.
Configuring
Snapshot options
Options can be defined by creating getSnapshotOptions()
method on model:
public function getSnapshotOptions(): SnapshotOptions { return SnapshotOptions::defaults(); }
Configurable options include:
withVersionist(Versionist $versionist)
- set Versionist usedsnapshotExcept(array $exclude)
- exclude attributes from being storedsnapshotHidden(bool $option = true)
- store hidden attributessnapshotDuplicate(bool $option = true)
- force snapshot even if the same already exists
Most can be later overridden while snapshotting using those methods:
version(Closure $closure)
- Closure that will receive current Versionist object, so you can access and call its methods if neededdescription(?string)
- add optional short descriptionsetExcept(array $except)
,appendExcept(array $except)
,removeExcept(array $except)
- modify excluded attributes listwithHidden()
,withoutHidden()
- modify if hidden attributes should be snapshottedforceDuplicate()
,noDuplicate()
- if snapshot should be forced even if duplicate already exists
Versionist
Versionist is a class responsible for determining next snapshot version. There are 2 classes available by default:
IncrementingVersionist
- increments versionsSemanticVersionist
- keeps versions inmajor.minor
format
If you would like to create your own versionist class it must implement
EriBloo\LaravelModelSnapshots\Contracts\Versionist
with methods:
public function getFirstVersion(): string; public function getNextVersion(string $version): string;
Traits
While no trait is needed to make a snapshot, package provides 2 helper traits for retrieving snapshots:
HasSnapshots
- providessnapshots()
relationship for retrieving stored snapshots as well as few getters:getLatestSnapshot()
getSnapshotByVersion(string $version)
- returns snapshot by specific versiongetSnapshotByDate(DateTimeImmutable $date)
- returns last snapshot created before date
HasSnapshotRelations
- provides relationship methods for creating connections with snapshots:morphSnapshots(string $snapshotClass)
- helpermorphToMany
morphSnapshot(string $snapshotClass)
- helpermorphToOne
morphSnapshotAsModels(string $snapshotClass)
-morphToMany
that returns snapshots withtoModels(false)
appliedmorphSnapshotAsModel(string $snapshotClass)
-morphToOne
that returns snapshot withtoModel(false)
applied
Events
There are a few events that get dispatched:
SnapshotCommitted
- dispatched when new snapshot is committed, but not when duplicate is foundSnapshotReverted
- dispatched when snapshot is revertedSnapshotBranched
- dispatched when new snapshot branch is createdSnapshotForked
- dispatched when snapshot is forked
Testing
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
License
The MIT License (MIT). Please see License File for more information.