⏱️Make Laravel model versionable

Overview

laravel-versionable

⏱️ Make Laravel model versionable.

Build Status Latest Stable Version Latest Unstable Version Scrutinizer Code Quality Code Coverage Total Downloads License

It's a minimalist way to make your model support version history, and it's very simple to roll back to the specified version.

Sponsor me

Requirement

  1. PHP >= 7.4
  2. laravel/framework >= 5.8|6.0|7.0

Features

  • Keep the specified number of versions.
  • Whitelist and blacklist for versionable attributes.
  • Easily roll back to the specified version.
  • Record only changed attributes.
  • Easy to customize.

Installing

$ composer require overtrue/laravel-versionable -vvv

Optional, you can publish the config file:

$ php artisan vendor:publish --provider="Overtrue\LaravelVersionable\ServiceProvider" --tag=config

And if you want to custom the migration of the versions table, you can publish the migration file to your database path:

$ php artisan vendor:publish --provider="Overtrue\LaravelVersionable\ServiceProvider" --tag=migrations

After you published the migration files, please update 'migrations' => false in the config file config/versionable.php to disable load the package migrations.

Then run this command to create a database migration:

$ php artisan migrate

Usage

Add Overtrue\LaravelVersionable\Versionable trait to the model and set versionable attributes:

use Overtrue\LaravelVersionable\Versionable;

class Post extends Model
{
    use Versionable;
    
    /**
     * Versionable attributes
     *
     * @var array
     */
    protected $versionable = ['title', 'content'];
    
    <...>
}

Versions will be created on vensionable model saved.

$post = Post::create(['title' => 'version1', 'content' => 'version1 content']);
$post->update(['title' => 'version2']);

Get versions

Get all versions

$post->versions;

Get latest version

$post->latestVersion;
// or
$post->lastVersion;

Reversion

Reversion a model instance to the specified version:

$post->getVersion(3)->revert();

// or

$post->revertToVersion(3);

Reversion without saving

$version = $post->versions()->first(); 

$post = $version->revertWithoutSaving();

Remove versions

// soft delete
$post->removeVersion($versionId = 1);
$post->removeVersions($versionIds = [1, 2, 3]);
$post->removeAllVersions();

// force delete
$post->forceRemoveVersion($versionId = 1);
$post->forceRemoveVersions($versionIds = [1, 2, 3]);
$post->forceRemoveAllVersions();

Restore deleted version by id

$post->restoreTrashedVersion($id);

Temporarily disable versioning

// create
Post::withoutVersion(function () use (&$post) {
    Post::create(['title' => 'version1', 'content' => 'version1 content']);
});

// update
Post::withoutVersion(function () use ($post) {
    $post->update(['title' => 'updated']);
});

Custom Version Store strategy

You can set the following different version policies through property protected $versionStrategy:

  • Overtrue\LaravelVersionable::DIFF - Version content will only contain changed attributes (Default Strategy).
  • Overtrue\LaravelVersionable::SNAPSHOT - Version content will contain all versionable attributes values.

❤️ Sponsor me

Sponsor me

如果你喜欢我的项目并想支持它,点击这里 ❤️

Project supported by JetBrains

Many thanks to Jetbrains for kindly providing a license for me to work on this and other open-source projects.

Contributing

You can contribute in one of three ways:

  1. File bug reports using the issue tracker.
  2. Answer questions or fix bugs on the issue tracker.
  3. Contribute new features or update the wiki.

The code contribution process is not very formal. You just need to make sure that you follow the PSR-0, PSR-1, and PSR-2 coding guidelines. Any new code contributions must be accompanied by unit tests where applicable.

License

MIT

Comments
  • Multiple Database Support

    Multiple Database Support

    I have been working on a project that utilizes multiple database connections and I want versioning within all of the databases.

    I published the migrations and updated them to create the version tables within each of the databases. This worked perfectly, however, when saving or deleting a records from a versionable table it only puts records into the default database which is not the desired outcome.

    It would be useful if the versionable trait would use a pass through of the connection from the model to be used on the version model.

    This would require that the versions table exist in any database with a versionable table contained within it, but my opinion is that the versions table should exist within all databases regardless of use (within that database).

    I am currently looking through this packages code to implement a possible solution to make this work, but I have not come up with anything at the moment.

    opened by ryangurn 4
  • Question: Syntax for blacklisting

    Question: Syntax for blacklisting

    In the readme, it is mentioned that specific attributes can be blacklisted instead of whitelisting a variety of fields.

    Can you please explain how this is done since it is not explicit in the readme about the process.

    opened by ryangurn 3
  • lastVersion and latestVersion should return the latest and not the first

    lastVersion and latestVersion should return the latest and not the first

    The docs are misleading. When I call:

    $model->lastVersion;
    

    I am expecting to get the latest version but I get the first. I see in the sources that it returns latest() relationship, so to get the latest version I have to use:

    $model->lastVersion()->first();
    

    In this case I get what I expect. Suggestion: Edit the docs or update the code

    opened by 4unkur 3
  • Versions are being created even if the versionable fields are not changed with snapshot strategy

    Versions are being created even if the versionable fields are not changed with snapshot strategy

    For the demo purpose I have created a repo with example. Just clone and install the dependencies. It's a fresh Laravel 9 app. Versionable model:

    https://github.com/4unkur/laravel-versionable-example/blob/master/app/Models/SurveyTemplate.php

    has 2 versionable fields. The strategy is SNAPSHOT

    Failing test: https://github.com/4unkur/laravel-versionable-example/blob/master/tests/Feature/VersionTest.php I'm expecting that no versions will be created if the versionable fields are not touched. I don't think Snapshot strategy is the problem as in my understanding it is related to the data stored in the versions: all versionable fields or just changed ones.

    opened by 4unkur 2
  • Is there any way to save the first record?

    Is there any way to save the first record?

    Hello, when I create a record, for example a user with name "John", and it changes it to "Charles", is there any way to get the original first name, "John"? Because it seems that I can just get the changes, in this case, "Charles", but then I lose a lot of information for that first change.

    Thank you so much.

    opened by Viturbiko 2
  • Incorrect usage of revertWithoutSaving in the docs

    Incorrect usage of revertWithoutSaving in the docs

    https://github.com/overtrue/laravel-versionable#reversion-without-saving

    here it says that the method can be used on the model but in fact the method belongs to the Version model, so the correct way of usage would be:

    $version = $post->versions()->first(); // just as an example
    $version->revertWithoutSaving();
    
    opened by 4unkur 2
  • Allow adding versions manually

    Allow adding versions manually

    This introduces the possibility to add versions manually by using createVersion() on the versionable model. This is useful if one would like to tweak a models history later on and add historical data from the past.

    $post->createVersion(); // will do nothing if the model is not dirty
    
    $post->title = 'Changed title';
    $post->createVersion(); // will create a version even if versioning is disabled and the model changes are not stored (yet)
    
    $post->createVersion(['title' => 'Changed title']); // will create a version of the model merged with the given attributes
    
    $post->createVersion(['title' => 'very old version'], '1970-01-01') // allows to create historic versions
    

    Because users are allowed to tweak a models version history later on the sorting has changed quite a bit. We are no longer sorting by Version.id alone. Instead created_at and id are taken into account to find previous and next versions.

    And because $model->versions isn’t guaranteed to be sorted correctly anymore $model->history does exactly that:

    $post->versions;
    // ['id' => 1, 'title' => 'First title', 'created_at' => '2022-10-07 12:15:00']
    // ['id' => 2, 'title' => 'Changed title', 'created_at' => '2022-10-07 12:30:00']
    // ['id' => 3, 'title' => 'very old version', 'created_at' => '1970-01-01 00:00:00']
    
    $post->history;
    // ['id' => 2, 'title' => 'Changed title', 'created_at' => '2022-10-07 12:30:00']
    // ['id' => 1, 'title' => 'First title', 'created_at' => '2022-10-07 12:15:00']
    // ['id' => 3, 'title' => 'very old version', 'created_at' => '1970-01-01 00:00:00']
    

    Please check out the tests I've added which will give a good overview of the changes.

    opened by marijoo 1
  • Add method to find a models version at a specific time

    Add method to find a models version at a specific time

    This PR adds a versionAt() method which accepts a Carbon parseable timestamp and queries for the version that was valid at this specific time. Will return null if the timestamp is before the first version and will return the latest version if the timestamp is after the last one.

    Also added some tests in VersionAtTest.php.

    opened by marijoo 1
  • Error when updating or saving from job

    Error when updating or saving from job

    Hello, I have an issue when using this package through a job. I think the problem is in this line:

    $version->{\config('versionable.user_foreign_key')} = \auth()->id(); line 64 in Version.php.

    When running a job, auth()->id() returns null, so it throws an error and it doesnt save changes. Is there any way to fix this?

    Thanks a lot.

    opened by Viturbiko 1
  • Fix documentation of VersionStrategy

    Fix documentation of VersionStrategy

    The class names mentioned in the Custom Version Store strategy section of the README is incorrect.

    - `Overtrue\LaravelVersionable::DIFF` - Version content will only contain changed attributes (Default Strategy).
    - `Overtrue\LaravelVersionable::SNAPSHOT` - Version content will contain all versionable attributes values.
    
    + `Overtrue\LaravelVersionable\VersionStrategy::DIFF` - Version content will only contain changed attributes (default strategy).
    + `Overtrue\LaravelVersionable\VersionStrategy::SNAPSHOT` - Version content will contain all versionable attributes values.
    
    opened by phpfour 0
  • Using passthrough of model connection.

    Using passthrough of model connection.

    Code change for #36.

    This will allow for multiple database connections where the connection from the model with the versionable trait will pass through to the version model.

    It does require that the version table exists in any database with a table that is versionable but that is the intent of this change.

    When there is no connection specied (ie: null connection name) Laravel will just use the default connection.

    opened by ryangurn 0
Releases(4.2.1)
Owner
安正超
Keep calm and coding.
安正超
A laravel package to generate model hashid based on model id column.

Laravel Model Hashid A package to generate model hash id from the model auto increment id for laravel models Installation Require the package using co

Touhidur Rahman 13 Jan 20, 2022
A package to filter laravel model based on query params or retrieved model collection

Laravel Filterable A package to filter laravel model based on query params or retrived model collection. Installation Require/Install the package usin

Touhidur Rahman 17 Jan 20, 2022
Laravel-model-mapper - Map your model attributes to class properties with ease.

Laravel Model-Property Mapper This package provides functionality to map your model attributes to local class properties with the same names. The pack

Michael Rubel 15 Oct 29, 2022
Make your own custom cast type for Laravel model attributes

Laravel Custom Casts Make your own cast type for Laravel model attributes Laravel custom casts works similarly to Eloquent attribute casting, but with

Vladimir Ković 220 Oct 28, 2022
A Laravel package that adds a simple image functionality to any Laravel model

Laraimage A Laravel package that adds a simple image functionality to any Laravel model Introduction Laraimage served four use cases when using images

Hussein Feras 52 Jul 17, 2022
Laravel comments - This package enables to easily associate comments to any Eloquent model in your Laravel application

Laravel comments - This package enables to easily associate comments to any Eloquent model in your Laravel application

Rubik 4 May 12, 2022
This Laravel package merges staudenmeir/eloquent-param-limit-fix and staudenmeir/laravel-adjacency-list to allow them being used in the same model.

This Laravel package merges staudenmeir/eloquent-param-limit-fix and staudenmeir/laravel-adjacency-list to allow them being used in the same model.

Jonas Staudenmeir 5 Jan 6, 2023
Easily create a revision history for any laravel model

Wouldn't it be nice to have a revision history for any model in your project, without having to do any work for it. By simply adding the RevisionableT

Venture Craft 2.4k Jan 6, 2023
laravel-model-validator

laravel-model-validator This is a simple validator. The validator can be created by command. The validator has all common table column constraint, eg:

null 6 May 22, 2022
Automatic Laravel model migrations.

Laravel Automatic Migrations Automatic Laravel model migrations. Instead of having to create and manage migration files, this package allows you to sp

null 38 Nov 11, 2022
Update multiple Laravel Model records, each with it's own set of values, sending a single query to your database!

Laravel Mass Update Update multiple Laravel Model records, each with its own set of values, sending a single query to your database! Installation You

Jorge González 88 Dec 31, 2022
Collection of the Laravel/Eloquent Model classes that allows you to get data directly from a Magento 2 database.

Laragento LAravel MAgento Micro services Magento 2 has legacy code based on abandoned Zend Framework 1 with really ugly ORM on top of outdated Zend_DB

Egor Shitikov 87 Nov 26, 2022
Laravel Quran is static Eloquent model for Quran.

Laravel Quran بِسْمِ ٱللّٰهِ الرَّحْمٰنِ الرَّحِيْمِ Laravel Quran is static Eloquent model for Quran. The Quran has never changed and never will, bec

Devtical 13 Aug 17, 2022
Save Model is a Laravel package that allows you to save data in the database in a new way.

Save Model is a Laravel package that allows you to save data in the database in a new way. No need to worry about $guarded and $fillable properties in the model anymore. Just relax an use Save Model package.

Laratips 27 Mar 2, 2022
PHP 8 attribute to register Laravel model observers.

PHP 8 attribute to register Laravel model observers. Instead of defining observers inside service providers this package offers an alternative way to

gpanos 15 May 30, 2022
A laravel package to attach uuid to model classes

Laravel Model UUID A simple package to generate model uuid for laravel models Installation Require the package using composer: composer require touhid

null 10 Jan 20, 2022
A simple laravel package to handle multiple key based model route binding

Laravel Model UUID A simple package to handle the multiple key/column based route model binding for laravel package Installation Require the package u

null 13 Mar 2, 2022
A package for Laravel One Time Password (OTP) generator and validation without Eloquent Model, since it done by Cache.

Laravel OTP Introduction A package for Laravel One Time Password (OTP) generator and validation without Eloquent Model, since it done by Cache. The ca

Lim Teck Wei 52 Sep 6, 2022
Store and retrieve settings generally or for model objects in Laravel.

Store and retrieve settings generally or for model objects in Laravel. Documentation You can find the detailed documentation here in Laravel Settings

Pharaonic 7 Dec 19, 2022