Model Revisions

Create revisions for any Eloquent model record along with its underlying relationships.

Usage

Your models should use the Varbox\Traits\HasRevisions trait and the Varbox\Options\RevisionOptions class. The trait contains an abstract method getRevisionOptions() that you must implement yourself.

Here's an example of how to implement the trait:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Varbox\Options\RevisionOptions;
use Varbox\Traits\HasRevisions;

class YourModel extends Model
{
    use HasRevisions;

    /**
     * Get the options for revisioning the model.
     *
     * @return RevisionOptions
     */
    public function getRevisionOptions(): RevisionOptions
    {
        return RevisionOptions::instance();
    }
}

Create Revisions Automatically

Once you've used the HasRevisions trait in your eloquent models, each time you update a model record, a revision containing its original attribute values will be created automatically using the updated eloquent event. The revision record will be stored inside the revisions database table.

$model = YourModel::find($id);
$model->update(...);

// the model has been modified
// a revision containing the model's original data is created

Create Revisions Manually

You can also create a revision manually by using the saveAsRevision() method present on the trait.

$model = YourModel::find($id);

// a new entry is stored inside the "revisions" database table
// reflecting the current state of that model record
$model->saveAsRevision();

Rollback To Revision

You can rollback the model record to one of its past revisions by using the rollbackToRevision() method present on the trait.

$model = YourModel::find($id);
$revision = $model->revisions()->latest()->first();

$model->rollbackToRevision($revision);

Fetch Revisions

You can fetch a model record's revisions by using the revisions() morph many relation present on the trait.

$model = YourModel::find($id);
$revisions = $model->revisions;

Customizations

The revision functionality offers a variety of customizations to fit your needs.

Limit Revisions Number

You can limit the number of revisions each model record can have by using the limitRevisionsTo() method in your definition of the getRevisionOptions() method.

When the limit is reached, after creating the new (latest) revision, the script automatically removes the oldest revision that model record has. This prevents ending up with thousands of revisions for a heavily updated record.

/**
 * Set the options for the HasRevisions trait.
 *
 * @return RevisionOptions
 */
public function getRevisionOptions(): RevisionOptions
{
    return RevisionOptions::instance()
        ->limitRevisionsTo(30);
}

Attach Timestamps To Revisions

By default, when creating a revision, the model's timestamps are excluded from the revision data.

If you'd like to store the model's timestamps when creating a revision, use the withTimestamps() method in your definition of the getRevisionOptions() method.

/**
 * Set the options for the HasRevisions trait.
 *
 * @return RevisionOptions
 */
public function getRevisionOptions(): RevisionOptions
{
    return RevisionOptions::instance()
        ->withTimestamps();
}

Include Fields To Revision

You can specify which fields to store when creating a new revision using the fieldsToRevision() method in your definition of the getRevisionOptions() method.

Please note that the excluded attributes won't be stored when creating the revision, but when rolling back, the excluded attributes will become null / empty for the actual model record.

/**
 * Get the options for revisioning the model.
 *
 * @return RevisionOptions
 */
public function getRevisionOptions(): RevisionOptions
{
    return RevisionOptions::instance()
        ->fieldsToRevision('title', 'content');
}

Exclude Fields From Revision

You can exclude certain model attributes when creating a revision using the fieldsNotToRevision() method in your definition of the getRevisionOptions() method.

The fieldsToRevision() takes precedence over the fieldsNotToRevision()
Don't use both methods in the same definition of the getRevisionOptions() method

Please note that the excluded attributes won't be stored when creating the revision, but when rolling back, the excluded attributes will become null / empty for the actual model record.

/**
 * Set the options for the HasRevisions trait.
 *
 * @return RevisionOptions
 */
public function getRevisionOptions(): RevisionOptions
{
    return RevisionOptions::instance()
        ->fieldsNotToRevision('title', 'content');
}

Include Relations To Revision

More often than not you will want to create a full copy in time of the model record and this includes revisioning its relations too (especially child relations).

When rolling back the model record to a past revision, the specified relations will also be rolled back to their state when that revision happened. This includes:

  • re-creating a relation record from ground up if it was force deleted
  • deleting any future added related records up until the revision checkpoint.

You can specify which relations to revision alongside the model record by using the relationsToRevision() method in your definition of the getRevisionOptions() method.

/**
 * Set the options for the HasRevisions trait.
 *
 * @return RevisionOptions
 */
public function getRevisionOptions(): RevisionOptions
{
    return RevisionOptions::instance()
        ->relationsToRevision('posts', 'payments');
}

Save Revision On Create

By default, when creating a new model record, a revision will not be created because the record is fresh and it's in its initial state.

You can create a revision when creating the model record using the enableRevisionOnCreate() method in your definition of the getRevisionOptions() method.

/**
 * Set the options for the HasRevisions trait.
 *
 * @return RevisionOptions
 */
public function getRevisionOptions(): RevisionOptions
{
    return RevisionOptions::instance()
        ->enableRevisionOnCreate();
}

Do Not Save Revision On Rollback

By default, when rolling back to a past revision, a new revision is automatically created. This new revision contains the model record's state before the rollback happened.

You can disable this behavior by using the disableRevisioningWhenRollingBack() method in your definition of the getRevisionOptions() method.

/**
 * Set the options for the HasRevisions trait.
 *
 * @return RevisionOptions
 */
public function getRevisionOptions(): RevisionOptions
{
    return RevisionOptions::instance()
        ->disableRevisioningWhenRollingBack();
}

The Model

Up until this point you've learned how to use the functionality exposed by the HasRevisions trait.
However, there's also a Varbox\Models\Revision model that manages the revisions.

Below you'll find some basic functionalities of the Varbox\Models\Revision model, but you can always inspect the model yourself, or even extend it.

Get Original Model

You can get a revision's original model by using the revisionable relation method.

use \Varbox\Models\Revision;

$revision = Revision::find($id);
$model = $revision->revisionable;

Get Revision Creator

You can get a revision's user by using the user relation method.

use \Varbox\Models\Revision;

$revision = Revision::find($id);
$user = $revision->user;

Get User Revisions

You can filter revisions by a user using the ofUser query scope present on the model.

use \Varbox\Models\Revision;

// by passing the loaded user model
$revisions = Revision::ofUser($user)->get();

// or by passing the user id
$revisions = Revision::ofUser($user->id)->get();

Eloquent Events

The revision functionality comes packed with two eloquent events: revisioning and revisioned

You can implement these events in your eloquent models as you would implement any other eloquent events that come with the Laravel framework.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Varbox\Options\RevisionOptions;
use Varbox\Traits\HasRevisions;

class YourModel extends Model
{
    use HasRevisions;

    /**
     * Boot the model.
     *
     * @return RevisionOptions
     */
    public static function boot()
    {
        parent::boot();

        static::revisioning(function ($model) {
            // your logic here
        });

        static::revisioned(function ($model) {
            // your logic here
        });
    }

    /**
     * Set the options for the HasRevisions trait.
     *
     * @return RevisionOptions
     */
    public function getRevisionOptions(): RevisionOptions
    {
        return RevisionOptions::instance();
    }
}

Overwrite Bindings

In your projects, you may stumble upon the need to modify the behavior of these classes, in order to fit your needs. Varbox makes this possible via the config/varbox/bindings.php configuration file. In that file, you'll find every customizable class the platform uses.

For more information on how the class binding works, please refer to the Custom Bindings documentation section.

Varbox\Models\Revision

Found in config/varbox/bindings.php at models.revision_model key.
This class represents the revision model.

Varbox\Controllers\RevisionsController

Found in config/varbox/bindings.php at controllers.revision_controller key.
This class is used for interactions with revisions inside an admin implementation.

Implementation Example

For an implementation example of this functionality please refer to the Full Example page.