Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

warpig's avatar
Level 12

How to make "draft" feature?

I was able to get this answer from 3 years ago: https://laracasts.com/discuss/channels/laravel/how-would-you-make-a-draft-feature

I have a few questions:

So if I set up a new "status" column on my "posts" table, does this column have to be of boolean type and be set to null? Do I need a pivoting table, how do I keep a post from being rendered just by checking if the status is set to 1 or 0? So if its 1, the post should be hidden or if its set to 0, it should not be hidden.

This is how I think the relationship looks:

  • A post "has one" status, its either a draft, or its a "published post", if its set as draft, hide it.
  • On my form, I write a post but I just need a draft for now, and I see 1 button, "Publish", so new "Save as Draft" button gets created.
  • In this new button I made, I make it hit that column and change the status to 1 because it's a draft, button "Publish" should change the status to 0, or it shouldn't do nothing at all with that column.

The way im managing this on my CRUD view is by rendering a list of all posts, I can delete or update but I do want to have a "Draft" list so i can continue the writing using a button or an <a>, should I have a function that looks for any post whos status is set to 1?

    public function store(Request $request)
    {
        $this->validatePost();

        $post = new Post(request(['title', 'body', 'slug', 'status']));

	if($post->status = true) {
	// write on the column
	// hide the post from being published
	// continue with the request
	}

        $post->user_id = 1;
        
        $post->tags()->attach(request('tags'));
        
        return redirect('/posts');
    }
    protected function validatePost() 
    {
        return request()->validate([
            'title' => 'required|max:255',
            'body'  => 'required',
            'slug'  => 'required|max:100',
            'tags' => 'exists:tags,id',
            'status' => 'required'
        ]);
    }

Im looking to have a basic setup here, as a first approach to the idea.

0 likes
9 replies
warpig's avatar
Level 12

On the Post model:

    public function status()
    {
        return $this->hasOne('App\Models\Status');
    }

On the Status model:

    public function post()
    {
        return $this->belongsTo('App\Models\Post');
    }
assoft's avatar

Assign statuses to Eloquent models

Imagine you want to have an Eloquent model hold a status. It's easily solved by just adding a status field to that model and be done with it. But in case you need a history of status changes or need to store some extra info on why a status changed, just adding a single field won't cut it.

https://github.com/spatie/laravel-model-status

warpig's avatar
Level 12

I dont need to add a history to the status, just need a draft status, which means just save it to the database, and let it be updated without a due date, until a button triggers an "approve" method, then let it be rendered and "published". I know it might sound weird, but im only a designer, I might not know a lot to this to intuitively get this just by reading code. It would be so wonderful if you could provide an example. Thank you so much.

warpig's avatar
Level 12

Iv'e done my homework though and have been researching. Apparently Im gonna need 2 functions to the controller where the column gets hit by these functions and changes its status from false to true.

    public function pending()
    {
        $post = Post::where('is_approved', false)->get();
        return view('/dashboard/posts', ['posts' => $post]);
    }
    public function approval($id)
    {
        $post = Post::find($id);
        if($post->is_approved == false) 
        {
            $post->is_approved = true;
            $post->save();
        } else {
            $post->with('This post has been approved');
        }

        return redirect('/posts');
    }

Also on the routes it is specified as:


Route::get('/pending/post', 'PostsController@pending')->middleware('auth');
Route::post('/posts/{id}/approve', 'PostsController@approval')->middleware('auth');

And then on the actual button:

<form method="POST" action="pending/post/{{$post->id}}">
	@csrf
	@method('PUT')
	<button class="submit-post">Save as Draft</button>
</form>
warpig's avatar
Level 12

I have tried this approach by watching a tutorial-video, and in there (which was in Hindu, which I don't speak) models were not mentioned, just a form element with a nested button, the routes, and 2 functions from the controller.

lacasera's avatar

I will simply add is_published which will be a boolean with a default value of false. Then write a scope to fetch published and drafted posts

    //Post Model
    //scope for published posts
    public function scopePublished($query)
    {
        return $query->where('is_published', '=',  true);
    }


    //scope for drafted posts
    public function scopeDrafted($query)
    {
        return $query->where('is_published', '=', false);
    }
//get published all published post


$publishedPosts = Post::published()->all()


//get drafted posts
$drafts = Post::drafted()->all()
1 like
assoft's avatar

Is this what you're looking for?

public function index($status = false, $published = false) {
    return Post::orderByDesc('created_at')
        ->when($status, function($query) use ($status) {
            $query->where("is_draft", $status)
        })->when($published, function($query) {
            $query->whereNotNull("published_at")
        })->get()
}
warpig's avatar
Level 12

Hi, yea that might be exactly what im looking for but, please don't get me wrong I already have an index function with a request for 'tags', how do you think it's best to incorporate the 2 things? Or should it be a separate function.

    public function index()
    {   
        if (request('tag')) {
        $post = Tag::where('name', request('tag'))
            ->firstOrFail()->posts;
    } else {
        $post = Post::latest()->simplePaginate(13);
    }   
        return view('posts.index', ['posts' => $post]);
    }
assoft's avatar

I am sorry that I do not have enough English language knowledge.

For this reason, I may not fully understand you or express myself fully.

It could be better this way:

Note: The request and table fields are representative. You should arrange it for yourself

public function index() {
    $posts = Post::orderByDesc('created_at')
        ->when(request()->has('status'), function($query) {
            $query->where("is_draft", request()->status)
        ->when(request()->has('published'), function($query) {
            $query->whereNotNull("published_at")
        })->when(request()->has('tags'), function($query) {
            $query->whereHas(["tags" => function($query) {
                $query->whereIn("tag_name", request()->tags)
            }]);
        })->paginate(13);
    
    return view('posts.index', ['posts' => $post]);
}

Please or to participate in this conversation.