oddvalue/laravel-drafts
Drop-in drafts and revisions for Laravel Eloquent models. Create, save, publish, and preview revisions with a simple API, middleware support, and minimal setup—ideal for CMS-style editing workflows without building a custom versioning system.
composer require oddvalue/laravel-drafts
php artisan vendor:publish --tag="drafts-config"
use Oddvalue\LaravelDrafts\Concerns\HasDrafts;
class Post extends Model
{
use HasDrafts;
}
Schema::table('posts', function (Blueprint $table) {
$table->drafts();
});
Create a draft post:
$post = Post::createDraft(['title' => 'Draft Post', 'content' => 'Draft content']);
Publish it:
$post->publish();
Draft Creation & Management:
createDraft() for new drafts.saveAsDraft()/updateAsDraft() for existing records.$post = Post::find(1);
$post->updateAsDraft(['title' => 'Updated Draft']);
Revisions Handling:
$revisions = Post::find(1)->revisions()->get();
$post->withoutRevision()->update(['title' => 'No Revision']);
Preview Mode:
\Oddvalue\LaravelDrafts\Facades\LaravelDrafts::previewMode();
\Oddvalue\LaravelDrafts\Facades\LaravelDrafts::disablePreviewMode();
Middleware for Draft Access:
Route::withDrafts(function () {
Route::get('/admin/posts', [PostController::class, 'index']);
});
Handling Relations:
protected $draftableRelations = ['tags', 'author'];
Custom Column Names:
public const IS_CURRENT = 'is_editing';
Query Scopes:
Post::onlyDrafts()->get();
Post::current()->get();
Relation Syncing:
HasOne, HasMany, BelongsToMany, and MorphToMany relations are supported. Ensure $draftableRelations is set correctly.Preview Mode Scope:
Revision Limits:
config/drafts.php:
'revisions' => ['keep' => 20],
Soft Deletes:
forceDelete() to bypass this.Check Current Revision:
$post->isCurrent(); // Returns true if the record is the current draft
Verify Draft State:
$post->isDraft(); // Returns true if unpublished
Inspect Revisions:
$post->revisions()->orderBy('created_at', 'desc')->get();
Customize Revision Behavior:
shouldCreateRevision() in your model:
protected function shouldCreateRevision(): bool
{
return !request()->has('skip_revision');
}
Modify Publish Logic:
publish() method to add custom logic before publishing.Event Listeners:
drafts.published or drafts.created events:
event(new \Oddvalue\LaravelDrafts\Events\Published($post));
Column Name Conflicts:
IS_CURRENT) don’t conflict with existing model attributes.Auth Guard:
auth.guard in config matches your application’s guard (default: web).UUID Generation:
Str::uuid(). Ensure your database supports UUID fields.Large Datasets:
withDrafts(false) or onlyDrafts() to limit query scope when fetching many records.Relation Loading:
Post::withDrafts()->with(['tags', 'author'])->get();
How can I help you explore Laravel packages today?