mansoor/filament-versionable
Filament plugin for managing Eloquent model revisions with Overtrue Laravel Versionable. View revision history, see diffs of what changed and who changed it, and restore any previous version from a dedicated Filament page.
Install the package:
composer require mansoor/filament-versionable
Publish migrations and config:
php artisan vendor:publish --provider="Overtrue\LaravelVersionable\ServiceProvider"
php artisan migrate
Configure your model:
use Overtrue\LaravelVersionable\Versionable;
use Overtrue\LaravelVersionable\VersionStrategy;
class Post extends Model
{
use Versionable;
protected $versionable = ['title', 'content'];
protected $versionStrategy = VersionStrategy::SNAPSHOT; // Critical for full attribute tracking
}
Add the RevisionsPage to your Filament Resource:
namespace App\Filament\Resources\PostResource\Pages;
use Mansoor\FilamentVersionable\RevisionsPage;
class PostRevisions extends RevisionsPage
{
protected static string $resource = PostResource::class;
}
Register the page in your Resource:
public static function getPages(): array
{
return [
'revisions' => Pages\PostRevisions::route('/{record}/revisions'),
];
}
Add the RevisionsAction to your Edit/View pages:
use Mansoor\FilamentVersionable\Page\RevisionsAction;
protected function getHeaderActions(): array
{
return [
RevisionsAction::make(),
];
}
Scenario: You want to track changes to a blog post and allow admins to restore previous versions.
Post in Filament.RevisionsAction).Model Configuration:
$versionable array to specify which attributes to track.VersionStrategy::SNAPSHOT for full attribute snapshots (avoid DIFF due to reported bugs).protected $versionable = ['title', 'content', 'published_at'];
Resource Integration:
RevisionsPage for each resource needing versioning.getPages() with a route like /{record}/revisions.'revisions' => Pages\PostRevisions::route('/{record}/revisions'),
UI Placement:
RevisionsAction::make() to Edit/View pages.RevisionsAction::make() to table actions for list views.$table->actions([
RevisionsAction::make(),
]);
Customization:
shouldStripTags() in your RevisionsPage:
public function shouldStripTags(): bool { return true; }
php artisan vendor:publish --tag="filament-versionable-views"
Conditional Versioning:
$versionable based on user roles or other logic:
public function getVersionable(): array
{
return auth()->user()->isAdmin() ? ['title', 'content', 'meta_description'] : ['title'];
}
Soft-Deletes Integration:
SoftDeletes and configure versioning to ignore soft-deleted states:
protected $versionable = ['title', 'content'];
protected $ignoreVersionableOn = ['deleted_at'];
Bulk Restore:
Version Metadata:
Version model to add custom metadata (e.g., change_reason):
use Overtrue\LaravelVersionable\Version;
class Version extends \Overtrue\LaravelVersionable\Version
{
protected $casts = [
'change_reason' => 'string',
];
}
Performance Optimization:
Version model:
protected static function booted()
{
static::addGlobalScope('limit_versions', function (Builder $builder) {
$builder->orderBy('created_at', 'desc')->take(50);
});
}
VersionStrategy::DIFF Issues:
VersionStrategy::DIFF due to reported bugs (e.g., missing changes or incorrect diffs).VersionStrategy::SNAPSHOT for reliability.Missing Revisions Tab:
RevisionsPage is properly registered in getPages().RevisionsAction is added to the page’s header/actions.UUID Primary Keys:
RevisionsPage accepts UUIDs in showVersion():
public function showVersion($version)
{
return parent::showVersion($version instanceof Uuid ? $version->toString() : $version);
}
HTML Diff Rendering:
content) may break diffs. Use shouldStripTags():
public function shouldStripTags(): bool { return true; }
Migration Conflicts:
versions table. Drop it manually if needed:
php artisan migrate:fresh --env=testing
Check Version Storage:
versions table:
php artisan tinker
>>> \App\Models\Post::find(1)->versions()->count();
Log Version Changes:
use Overtrue\LaravelVersionable\Events\VersionCreated;
VersionCreated::listen(function ($version) {
\Log::info("Version created for {$version->model_type}: {$version->version}");
});
Diff Not Showing:
$versionable attributes are not excluded (e.g., by accessors/mutators). Use raw attributes:
protected $versionable = ['title', 'content'];
protected $appends = []; // Avoid appending non-versionable fields
Restore Button Disabled:
canRestore policy).Custom Version Model:
Version model to add fields like ip_address or device_info:
class Version extends \Overtrue\LaravelVersionable\Version
{
protected $fillable = ['ip_address', 'user_agent'];
}
Version Query Scopes:
public function scopeRecent($query, $days = 30)
{
return $query->where('created_at', '>=', now()->subDays($days));
}
Custom Diff Renderer:
resources/views/vendor/filament-versionable/revisions/diff.blade.php.Event Listeners:
use Overtrue\LaravelVersionable\Events\VersionCreated;
VersionCreated::listen(function ($version) {
// Send email or Slack notification
});
Filament Policy Integration:
public static function canAccessRevisions(): bool
{
return auth()->user()->can('manage_revisions');
}
Theme Integration:
@import '../../../../vendor/mansoor/filament-versionable/resources/css/plugin.css';
@source '../../../../vendor/mansoor/filament-versionable/resources/**/*.blade.php';
addThemeStyle() method.Translation Keys:
php artisan vendor:publish --tag="filament-versionable-translations"
How can I help you explore Laravel packages today?