Installation:
composer require sofa/revisionable
Register Provider:
Add to config/app.php:
'providers' => [
// ...
Sofa\Revisionable\Laravel\ServiceProvider::class,
],
Publish Config:
php artisan vendor:publish --provider="Sofa\Revisionable\Laravel\ServiceProvider"
This generates config/sofa_revisionable.php with default settings.
Enable Revisions on Model:
Use the Revisionable trait in your Eloquent model:
use Sofa\Revisionable\RevisionableTrait;
class Post extends Model
{
use RevisionableTrait;
}
View all revisions for a model instance:
$post = Post::find(1);
$revisions = $post->revisions; // Collection of Revision models
Compare two revisions:
$revision1 = $post->revisions->first();
$revision2 = $post->revisions->last();
$diff = $revision1->diff($revision2); // Returns a diff array
created_at, updated_at, and all modified fields (including soft-deletes if enabled).revisionable array in your model:
class Post extends Model
{
use RevisionableTrait;
protected $revisionable = [
'title',
'content',
'-votes', // Exclude 'votes' field
];
}
$revisions = Post::find(1)->revisions;
$revisions = Post::find(1)->revisions()
->where('created_at', '>', now()->subDays(7))
->where('user_id', auth()->id())
->get();
$revision = Post::find(1)->revisions()->find(5); // Revision ID 5
$post = Post::find(1);
$post->restoreRevision($revisionId); // Restores to the state of revision ID
$post->rollbackRevision($revisionId);
$diff = $revision1->diff($revision2);
// Returns an array like:
// [
// 'title' => ['old' => 'Old Title', 'new' => 'New Title'],
// 'content' => ['old' => 'Old Content', 'new' => 'New Content'],
// ]
// In EventServiceProvider
protected $listen = [
'Sofa\Revisionable\Events\RevisionCreated' => [
'App\Listeners\LogRevision',
],
];
Revision model:
class CustomRevision extends \Sofa\Revisionable\Models\Revision
{
protected $table = 'custom_revisions';
}
'model' => \App\Models\CustomRevision::class,
use Sofa\Revisionable\RevisionableTrait;
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use RevisionableTrait, SoftDeletes;
protected $revisionable = ['title', 'content'];
}
sofa_revisionable.php:
'soft_deletes' => true,
revisionable trait hooks:
class Post extends Model
{
use RevisionableTrait;
protected static function bootRevisionableTrait()
{
static::revisionable(function ($model, $revision) {
$revision->ip_address = request()->ip();
});
}
}
class RevisionController extends Controller
{
public function index(Post $post)
{
return $post->revisions;
}
public function show(Post $post, Revision $revision)
{
return $revision;
}
}
$post = Post::factory()->create();
$post->revisions()->create([
'data' => ['title' => 'Old Title'],
'user_id' => 1,
]);
$revisionable.
Fix: Ensure the field exists in the database and is not excluded with a - prefix.
// Correct:
protected $revisionable = ['title', 'content'];
// Incorrect (excludes 'votes'):
protected $revisionable = ['title', '-votes'];
with() to eager-load relationships or paginate:
$revisions = Post::find(1)->revisions()->paginate(10);
soft_deletes is enabled in both the model and config:
'soft_deletes' => true,
'primary_key' => 'uuid',
created_at and updated_at not being tracked.
Fix: Ensure $timestamps = true in your model and include them in $revisionable:
protected $revisionable = ['*']; // Tracks all fields including timestamps
$revision = Post::find(1)->revisions()->first();
dd($revision->data); // Inspect stored data
DB::enableQueryLog();
$revisions = Post::find(1)->revisions;
dd(DB::getQueryLog());
RevisionableMiddleware is registered if using custom guards:
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
protected static function bootRevisionableTrait()
{
static::saving(function ($model) {
// Ensure no conflicting logic here
});
}
Revision model and overriding the save() method.class Post extends Model
{
use RevisionableTrait;
protected static function bootRevisionableTrait()
{
static::revisionable(function ($model, $revision) {
if ($model->isSignificantChange()) {
$revision->validate();
}
});
}
}
class Post extends Model
{
use RevisionableTrait;
protected static function bootRevisionableTrait()
{
static::revisionable(function ($model, $revision) {
Notification::send($model->user, new RevisionCreated($revision));
});
}
}
How can I help you explore Laravel packages today?