Polyscope - The agent-first dev environment for Laravel

Laravel Drafts

oddvalue/laravel-drafts image

Laravel Drafts stats

Downloads
10K
Stars
295
Open Issues
1
Forks
9

View on GitHub →

A simple, drop-in drafts/revisions system for Laravel models

Laravel Drafts

A simple, drop-in drafts/revisions system for Laravel models

Installation

You can install the package via composer:

composer require oddvalue/laravel-drafts

You can publish the config file with:

php artisan vendor:publish --tag="drafts-config"

This is the contents of the published config file:

return [
'revisions' => [
'keep' => 10,
],
 
'column_names' => [
/*
* Boolean column that marks a row as the current version of the data for editing.
*/
'is_current' => 'is_current',
 
/*
* Boolean column that marks a row as live and displayable to the public.
*/
'is_published' => 'is_published',
 
/*
* Timestamp column that stores the date and time when the row was published.
*/
'published_at' => 'published_at',
 
/*
* UUID column that stores the unique identifier of the model drafts.
*/
'uuid' => 'uuid',
 
/*
* Name of the morph relationship to the publishing user.
*/
'publisher_morph_name' => 'publisher',
],
 
'auth' => [
/*
* The guard to fetch the logged-in user from for the publisher relation.
*/
'guard' => 'web',
],
];

Usage

Preparing your models

Add the trait

Add the HasDrafts trait to your model

<?php
 
use Illuminate\Database\Eloquent\Model;
use Oddvalue\LaravelDrafts\Concerns\HasDrafts;
 
class Post extends Model
{
use HasDrafts;
 
...
}

Relations

The package can handle basic relations to other models. When a draft is published HasOne and HasMany relations will be duplicated to the published model and BelongsToMany and MorphToMany relations will be synced to the published model. In order for this to happen you first need to set the $draftableRelations property on the model.

protected array $draftableRelations = [
'posts',
'tags',
];

Alternatively you may override the getDraftableRelations method.

public function getDraftableRelations()
{
return ['posts', 'tags'];
}

Database

The following database columns are required for the model to store drafts and revisions:

  • is_current
  • is_published
  • published_at
  • uuid
  • publisher_type
  • publisher_id

The names of these columns can be changed in the config file or per model using constants

e.g. To alter the name of the is_current column then you would add a class constant called IS_CURRENT

<?php
 
use Illuminate\Database\Eloquent\Model;
use Oddvalue\LaravelDrafts\Concerns\HasDrafts;
 
class Post extends Model
{
use HasDrafts;
 
public const IS_CURRENT = 'admin_editing';
 
...
}

There are two helper methods added to the schema builder for use in your migrations that will add/remove all these columns for you:

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
 
Schema::table('posts', function (Blueprint $table) {
$table->drafts();
});
 
Schema::table('posts', function (Blueprint $table) {
$table->dropDrafts();
});

The API

The HasDrafts trait will add a default scope that will only return published/live records.

The following quiery builder methods are available to alter this behavior:

  • withoutDrafts()/published(bool $withoutDrafts = true) Only select published records (default)
  • withDrafts(bool $withDrafts = false) Include draft record
  • onlyDrafts() Select only drafts, exclude published

Creating a new record

By default, new records will be created as published. You can change this either by including 'is_published' => false in the attributes of the model or by using the createDraft or saveAsDraft methods.

Post::create([
'title' => 'Foo',
'is_published' => false,
]);
 
# OR
 
Post::createDraft(['title' => 'Foo']);
 
# OR
 
Post::make(['title' => 'Foo'])->saveAsDraft();

When saving/updating a record the published state will be maintained. If you want to save a draft of a published record then you can use the saveAsDraft and updateAsDraft methods.

# Create published post
$post = Post::create(['title' => 'Foo']);
 
# Create drafted copy
 
$post->updateAsDraft(['title' => 'Bar']);
 
# OR
 
$post->title = 'Bar';
$post->saveAsDraft();

This will create a draft record and the original record will be left unchanged.

# title uuid published_at is_published is_current created_at updated_at
1 Foo 9188eb5b-cc42-47e9-aec3-d396666b4e80 2000-01-01 00:00:00 1 0 2000-01-01 00:00:00 2000-01-01 00:00:00
2 Bar 9188eb5b-cc42-47e9-aec3-d396666b4e80 2000-01-02 00:00:00 0 1 2000-01-02 00:00:00 2000-01-02 00:00:00

Interacting with records

Published revision

The published revision if the live version of the record and will be the one that is displayed to the public. The default behavior is to only show the published revision.

# Get all published posts
$posts = Post::all();

Current Revision

Every record will have a current revision. That is the most recent revision and what you would want to display in your admin.

To fetch the current revision you can call the current scope.

$posts = Post::current()->get();

Revisions

Every time a record is updated a new row/revision will be inserted. The default number of revisions kept is 10, this can be updated in the published config file.

You can fetch the revisions of a record by calling the revisions method.

$post = Post::find(1);
$revisions = $post->revisions();

Deleting a record will also delete all of its revisions. Soft deleting records will soft delete the revisions and restoring records will restore the revisions.

If you need to update a record without creating revision

$post->withoutRevision()->update($options);

Preview Mode

Enabling preview mode will disable the global scope that fetches only published records and will instead fetch the current revision regardless of published state.

# Enable preview mode
\Oddvalue\LaravelDrafts\Facades\LaravelDrafts::previewMode();
\Oddvalue\LaravelDrafts\Facades\LaravelDrafts::previewMode(true);
 
# Disable preview mode
\Oddvalue\LaravelDrafts\Facades\LaravelDrafts::disablePreviewMode();
\Oddvalue\LaravelDrafts\Facades\LaravelDrafts::previewMode(false);

Middleware

WithDraftsMiddleware

If you require a specific route to be able to access drafts then you can use the WithDraftsMiddleware middleware.

Route::get('/posts/publish/{post}', [PostController::class, 'publish'])->middleware(\Oddvalue\LaravelDrafts\Http\Middleware\WithDraftsMiddleware::class);

There is also a helper method on the router that allows you to create a group with that middleware applied.

Route::withDrafts(function (): void {
Route::get('/posts/publish/{post}', [PostController::class, 'publish']);
});

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.

Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.


Oddvalue Laravel Drafts Related Articles

Craft CMS is moving to Laravel image

Craft CMS is moving to Laravel

Read article
Laravel's Enhanced String Validation with Inverse Methods image

Laravel's Enhanced String Validation with Inverse Methods

Read article
Statamic 4 Released image

Statamic 4 Released

Read article
Using Laravel Model Factories in your tests image

Using Laravel Model Factories in your tests

Read article
A Simple Draft and Revision System for Laravel image

A Simple Draft and Revision System for Laravel

Read article
Blastup logo

Blastup

Blastup provides social media enhancement services including buying Instagram likes, followers, and views, with features like instant delivery and a variety of packages to suit different needs.

Blastup
Tighten logo

Tighten

We help companies turn great ideas into amazing apps, products, and services.

Tighten
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Multi-tenant Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
DreamzTech logo

DreamzTech

Hire 6-10+ Yrs. experienced skilled Laravel Developers from DreamzTech. We ensure NDA protected, 100% quality delivery. Contact Us & Discuss Your Need.

DreamzTech
Curotec logo

Curotec

World class Laravel experts with GenAI dev skills. LATAM-based, embedded engineers that ship fast, communicate clearly, and elevate your product. No bloat, no BS.

Curotec