jpnut/eloquent-versioning

An extension for the Eloquent ORM to support versioning.

2.0.0 2020-02-10 13:56 UTC

This package is auto-updated.

Last update: 2024-05-11 01:38:58 UTC


README

Latest Version Quality Score Software License StyleCI

This package provides a trait that adds versions to an Eloquent model (forked from proai/eloquent-versioning).

New versions are created whenever a model is updated. Versions are stored in a separate table for each model (e.g. user_versions).

The package allows for a mixture of versioned and non-versioned attributes.

By default, queries are scoped to merge records in the parent table with the latest version. Additional scopes exist to merge records at a particular version or point in time.

Installation

This package can be installed through Composer.

composer require jpnut/eloquent-versioning

Usage

To add versions to your model you must:

  1. Implement the JPNut\Versioning\Versionable interface.

  2. Use the JPNut\Versioning\VersionableTrait trait.

  3. Add the getVersionableOptions method to your model. This method must return an instance of JPNut\Versioning\VersionOptions. You should define which attributes you would like to version by calling setVersionableAttributes and passing an array of attributes:

    ...
    
    /**
     * @return \JPNut\Versioning\VersionOptions
     */
    public function getVersionableOptions(): VersionOptions
    {
        return VersionOptions::create()
            ->setVersionableAttributes(['email', 'city']);
    }
    
    ...
  4. Add the version column to the table of the model which you wish to version. This keeps track of the current version.

    ...
    
    Schema::create('users', function (Blueprint $table) {
        $table->increments('id');
        $table->integer('version')->unsigned()->nullable();
        $table->string('name');
        $table->timestamps();
        $table->softDeletes();
    });
    
    ...
  5. Create a table to contain the versions. This table should contain a reference to the parent model (parent_id), the version number version, all versionable attributes (email, city), and the created_at timestamp.

    ...
    
    Schema::create('user_versions', function (Blueprint $table) {
        $table->integer('parent_id')->unsigned();
        $table->integer('version')->unsigned();
        $table->string('email');
        $table->string('city');
        $table->timestamp('created_at');
    
        $table->primary(['parent_id', 'version']);
    });
    
    ...

It is assumed that the version table name takes the form {entity}_versions where entity is the singular form of the entity noun (e.g. users and user_versions). It is possible to override this and all column names by calling the relevant method on the options object (the following shows the default settings).

...
   
/**
 * @return \JPNut\Versioning\VersionOptions
 */
public function getVersionableOptions(): VersionOptions
{
    return VersionOptions::create()
        ->saveVersionKeyTo('version')
        ->useVersionTable('user_versions')
        ->saveVersionTableKeyTo('version')
        ->versionTableForeignKeyName('parent_id')
        ->versionTableCreatedAtName('created_at');
}

...

Example

use Illuminate\Database\Eloquent\Model;
use JPNut\Versioning\Versionable;
use JPNut\Versioning\VersionableTrait;
use JPNut\Versioning\VersionOptions;

class YourEloquentModel extends Model implements Versionable
{
    use VersionableTrait;
    
    /**
     * @return VersionOptions
     */
    public function getVersionableOptions(): VersionOptions
    {
        return VersionOptions::create()
            ->setVersionableAttributes(['email', 'city']);
    }

    ...
}

You can retrieve the model at a specific version by using the atVersion scope

Model::atVersion(1)->find(1);

You can retrieve the model at a specific point in time by using the atTime scope

Model::atTime(now()->subDay())->find(1);

Note that this will attempt to find the last version created before the time supplied. If there are no such versions, the method will return null.

You can disable the global scope by using the withoutVersion scope

Model::withoutVersion()->find(1);

You can obtain all versions in the form of a relationship by calling the versions property (or method) on a model instance

$model->versions;

Note that by default a generic Version model is used. You can change this model by overwriting the versions method and returning your own HasMany relationship.

You can revert to a previous version by calling the changeVersion() method with the desired version as an argument.

$model->changeVersion(1);

Note that this creates a new version with the same versionable attribute values as the version specified (rather than changing the value of the version column in the parent table).

Tests

The package contains some integration tests, set up with Orchestra. The tests can be run via phpunit.

vendor/bin/phpunit

Contributing

Create a Pull Request!

Alternatives

License

The MIT License (MIT). Please see License File for more information.