Installation:
composer require indracollective/laravel-revisor
Publish the migration and config:
php artisan vendor:publish --provider="IndraCollective\Revisor\RevisorServiceProvider" --tag="migrations"
php artisan vendor:publish --provider="IndraCollective\Revisor\RevisorServiceProvider" --tag="config"
Run migrations:
php artisan migrate
Enable Revisor for a Model:
Use the Revisable trait in your Eloquent model:
use IndraCollective\Revisor\Traits\Revisable;
class Post extends Model
{
use Revisable;
}
First Use Case: Create a draft, publish it, and revise it:
$post = new Post(['title' => 'Draft Post']);
$post->save(); // Creates a draft
$post->publish(); // Publishes the draft
$post->title = 'Updated Post';
$post->save(); // Creates a revision
config/revisor.php (customize revision behavior, e.g., soft-deletes, revision limits).database/migrations/[timestamp]_create_revisions_table.php (understand the schema for revisions).Drafting and Publishing:
// Create a draft (unpublished)
$post = Post::create(['title' => 'Draft Title']);
// Publish the draft
$post->publish(); // Sets `published_at` and marks as live
// Revert to draft
$post->draft(); // Resets `published_at` and unpublishes
Revisions:
// Get all revisions for a model
$revisions = $post->revisions()->get();
// Restore a specific revision
$revision = $post->revisions()->where('revisionable_id', $post->id)->first();
$post->restore($revision);
// Compare revisions (e.g., for a diff view)
$diff = $post->revisions()->diff($revision);
Soft Deletes:
Enable in config (config/revisor.php):
'soft_deletes' => true,
Then use:
$post->delete(); // Soft deletes the latest revision
$post->forceDelete(); // Permanently deletes
Querying:
// Get published posts
Post::published()->get();
// Get drafts
Post::drafts()->get();
// Get a specific revision
$post->revisions()->find(1);
FilamentPHP: Use laravel-revisor-filament for a UI:
composer require indracollective/laravel-revisor-filament
Add to your Filament resources:
use IndraCollective\Revisor\Filament\Resources\RevisorResource;
RevisorResource::make();
Events: Listen for revision events (e.g., RevisablePublished, RevisableCreated):
public function boot()
{
\IndraCollective\Revisor\Events\RevisablePublished::subscribe(function ($model) {
// Log or notify when a model is published
});
}
API Responses: Serialize revisions in API responses:
$post->append(['revisions' => $post->revisions()->latest()->take(5)->get()]);
Testing: Use Revisable in feature tests:
$post = Post::factory()->create();
$post->publish();
$this->assertDatabaseHas('posts', ['id' => $post->id, 'published_at' => now()]);
Revision Limits:
config/revisor.php:
'revision_limits' => [
'posts' => 10, // Keep only 10 revisions for `Post` models
],
Soft Deletes Conflict:
SoftDeletes, ensure config/revisor.php has:
'soft_deletes' => true,
delete() to fail silently or behave unexpectedly.Mass Assignment:
revisionable_id, revision_number, etc. Add them to $guarded or $fillable explicitly:
protected $guarded = ['revisionable_id', 'revision_number'];
Foreign Key Constraints:
revisions table has a foreign key to your model’s table. If you rename your table, update the migration manually.Event Order:
RevisableCreated fires before RevisablePublished. Use this to set defaults:
RevisableCreated::subscribe(function ($model) {
$model->author_id = auth()->id();
});
Missing Revisions:
revisions table exists and has the correct schema.revisionable_type and revisionable_id are set correctly (debug with dd($post->revisions()->toSql())).Publish/Draft Not Working:
published_at is nullable in your model’s table:
$table->timestamp('published_at')->nullable();
published_at.Performance:
// Bad: Loads all revisions for every post
$posts = Post::with('revisions')->get();
// Good: Load revisions only when needed
$post = Post::find(1);
if (request()->wantsJson()) {
$post->load('revisions');
}
Custom Revision Fields:
Add custom fields to the revisions table by publishing and modifying the migration:
php artisan vendor:publish --provider="IndraCollective\Revisor\RevisorServiceProvider" --tag="migrations"
Then extend the Revisable trait or use a service provider to add logic.
Revision Strategies: Override the default revision strategy (e.g., to exclude certain fields):
class Post extends Model
{
use Revisable;
protected $revisionStrategy = \IndraCollective\Revisor\Strategies\ExcludeStrategy::class;
protected $revisionExcludes = ['votes', 'temp_data'];
}
Custom Revision Model:
Extend the Revision model to add behavior:
class CustomRevision extends \IndraCollective\Revisor\Models\Revision
{
public function scopeApproved($query)
{
return $query->where('approved', true);
}
}
Bind it in a service provider:
\IndraCollective\Revisor\Models\Revision::swap(new \App\Models\CustomRevision());
API Resources: Create a custom API resource for revisions:
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class RevisionResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'revision_number' => $this->revision_number,
'changes' => $this->changes,
'created_at' => $this->created_at,
];
}
}
Audit Logs:
Combine with spatie/laravel-activitylog to track who made revisions:
use Spatie\Activitylog\LogOptions;
$post->logActivity('updated', new LogOptions(['onlyChanges' => true]));
Diff Views:
Use laravel-revisor-filament or build a custom Blade view to show revision diffs:
$diff = $post->revisions()->diff($revision);
return view('posts.diff', compact('diff'));
Bulk Operations:
Use Revisable::publishMany() or `Revisable::draftMany
How can I help you explore Laravel packages today?