WordPress-Like Blog Laravel 5.7 and AdminLTE 3 (14) – Deleting a Post
In this thirteenth part of creating WordPress-Like Blog using Laravel 5.7 and AdminLTE 3, we will :
- Delete an Existing Post and put it in the Trash
- List Deleted Posts inside the Trash
- Delete image and image thumbnail
Delete and Existing Post
To delete a post, firstly we’ll modify resources/views/backend/blog index.blade.php and add Form open and Form close around this line of code :
1 2 3 4 5 6 |
<td> {!! Form::open(['method' => 'DELETE', 'route' => ['backend.blog.destroy', $post->id] ]) !!} <a href="{{ route('backend.blog.edit', $post->id) }}" class="btn btn-sm btn-default"><i class="fa fa-edit"></i></a> <button type="submit" class="btn btn-sm btn-danger"><i class="fa fa-times"></i></button> {!! Form::close() !!} </td> |
Next, let’s add some code inside destroy() method of Backend\BlogController.php
1 2 3 4 5 6 |
public function destroy($id) { Post::findOrFail($id)->delete(); return redirect('/backend/blog')->with('message', 'Your post has been deleted'); } |
Let’s try to delete one post from the index page :
This action will cause the post deleted from the database.
Instead of directly delete the post from the database, we will modify the code in order to put a staging area for the post before it permanently deleted. We need to add the soft deletion method to posts table. Create a migration from the terminal :
1 |
php artisan make:migration add_soft_deletion --table=posts |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public function up() { Schema::table('posts', function (Blueprint $table) { $table->softDeletes(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('posts', function (Blueprint $table) { $table->dropSoftDeletes(); }); } |
Run the migration, add this line to app\Post.php
1 2 3 4 5 |
use Illuminate\Database\Eloquent\SoftDeletes; class Post extends Model { use SoftDeletes; |
Now, if you delete the post, it will not disappear from the database table. It stays there with deleted_at field added instead.
We will put the deleted posts inside the trash. We will be able to undo deleted post and put it back to its original state. Modify destroy() method inside Backend\BlogController.php
1 2 3 4 5 6 |
public function destroy($id) { Post::findOrFail($id)->delete(); return redirect('/backend/blog')->with('trash-message', ['Your post has been moved to the trash', $id]); } |
Add message.blade.php inside backend/blog and move the message part from index.blade.php inside it :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@if(session('message')) <div class="alert alert-info"> {{ session('message') }} </div> @elseif(session('trash-message')) <div class="alert alert-info"> <?php list($message, $postId) = session('trash-message') ?> {{ $message }} {!! Form::open(['method'=>'PUT', 'route' => ['backend.blog.restore', $postId]]) !!} <button type="submit">Undo</button> {!! Form::close() !!} </div> @endif |
And put this inside the empty line on index.blade.php :
1 |
@include('backend.blog.message') |
Add new route to routes/web.php
1 2 3 4 |
Route::put('/backend/blog/restore/{blog}',[ 'uses' => 'Backend\BlogController@restore', 'as' => 'backend.blog.restore', ]); |
Modify Backend\BlogController.php and add restore() method :
1 2 3 4 5 6 |
public function restore($id){ $post = Post::withTrashed()->findOrFail($id); $post->restore(); return redirect('/backend/blog')->with('message', 'Your post has been restored'); } |
Now, if we delete a post, a notification and Undo button will appear. If you push it, the post will not be deleted.
List Deleted Posts Inside the Trash
The deleted posts will be displayed. Let’s modify index.blade.php beforehand :
1 2 3 4 5 6 |
<div class="col-md-6" style="padding-left: 10px; padding-right: 30px; padding-top: 10px; padding-bottom: 10px; "> <div class="float-right" style="color: blue;"> <a class="" href="?status=active">Active(12) </a> | <a class="" href="?status=trash">Trash </a> </div> </div> |
Open Backend\BlogController.php and modify index() method :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public function index(Request $request) { if (($status = $request->get('status')) && $status == 'trash'){ $posts = Post::onlyTrashed()->with('category','author')->latest()->paginate($this->limit); $allPostCount = Post::onlyTrashed()->count(); $onlyTrashed = TRUE; }else{ $posts = Post::with('category','author')->latest()->paginate($this->limit); $allPostCount = Post::count(); $onlyTrashed = FALSE; } return view("backend.blog.index", compact('posts','allPostCount','onlyTrashed')); } |
Modify index.blade.php, cut the code from <table> to </table> , create a new file table.blade.php and paste the code inside it :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<table class="table table-striped"> <tr> <th width="10%">Action </th> <th>Title</th> <th width="20%">Author</th> <th width="20%">Category</th> <th width="20%">Date</th> </tr> @foreach($posts as $post) <tr> <td> {!! Form::open(['method' => 'DELETE', 'route' => ['backend.blog.destroy', $post->id] ]) !!} <a href="{{ route('backend.blog.edit', $post->id) }}" class="btn btn-sm btn-default"><i class="fa fa-edit"></i></a> <button type="submit" class="btn btn-sm btn-danger"><i class="fa fa-trash"></i></button> {!! Form::close() !!} </td> <td>{{ $post->title }}</td> <td>{{ $post->author->name }}</td> <td>{{ $post->category->title }}</td> <td> <abbr title="{{ $post->formattedDate(true) }}"> {{ $post->formattedDate() }}</abbr> | {!! $post->publicationLabel() !!} </td> </tr> @endforeach </table> |
Create another file, table-trash.blade.php :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<table class="table table-striped"> <tr> <th width="10%">Action </th> <th>Title</th> <th width="20%">Author</th> <th width="20%">Category</th> <th width="20%">Date</th> </tr> @foreach($posts as $post) <tr> <td> {!! Form::open(['style' => 'display:inline-block' , 'method' => 'PUT', 'route' => ['backend.blog.restore', $post->id] ]) !!} <button title="Restore" class="btn btn-sm btn-default"><i class="fa fa-refresh"></i></button> {!! Form::close() !!} {!! Form::open(['style' => 'display:inline-block' ,'method' => 'DELETE', 'route' => ['backend.blog.force-destroy', $post->id] ]) !!} <button title="Hard Delete" type="submit" onclick="return confirm('Are you sure to delete the post?')" class="btn btn-sm btn-danger"><i class="fa fa-times"></i></button> {!! Form::close() !!} </td> <td>{{ $post->title }}</td> <td>{{ $post->author->name }}</td> <td>{{ $post->category->title }}</td> <td> <abbr title="{{ $post->formattedDate(true) }}"> {{ $post->formattedDate() }}</abbr> </td> </tr> @endforeach </table> |
Since we mentioned force-destroy route, we need to add it to routes/web.php
1 2 3 4 |
Route::delete('/backend/blog/force-destroy/{blog}',[ 'uses' => 'Backend\BlogController@forceDestroy', 'as' => 'backend.blog.force-destroy', ]); |
Add forceDestroy() method inside Backend\BlogController.php
1 2 3 4 5 |
public function forceDestroy($id){ Post::withTrashed()->findOrFail($id)->forceDelete(); return redirect('/backend/blog?status=trash')->with('message', 'The post has been deleted permanently'); } |
Delete Image and Image Thumbnail
Previously, we have successfully delete a post. However, if there was image for that post, we still haven’t delete it correctly. We will fix that problem.
Modify Backend\BlogController.php and create a new method, removeImage() :
1 2 3 4 5 6 7 8 9 10 11 |
private function removeImage($image){ if(!empty($image)){ $imagePath = $this->uploadPath . '/' . $image; $ext = substr(strrchr($image, '.'), 1); $thumbnail = str_replace(".{$ext}","_thumb.{$ext}", $image); $thumbnailPath = $this->uploadPath . '/' . $thumbnail; if (file_exists($imagePath) ) unlink($imagePath); if (file_exists($thumbnailPath) ) unlink($thumbnailPath); } } |
And also add this method to forceDestroy :
1 2 3 4 5 6 7 8 |
public function forceDestroy($id){ $post = Post::withTrashed()->findOrFail($id); $post->forceDelete(); $this->removeImage($post->image); return redirect('/backend/blog?status=trash')->with('message', 'The post has been deleted permanently'); } |
Also modify update() method :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public function update(Requests\PostRequest $request, $id) { $post = Post::findOrFail($id); $oldImage = $post->image; $data = $this->handleRequest($request); $post->update($data); if($oldImage !== $post->image) { $this->removeImage($oldImage); } return redirect(route('backend.blog.index'))->with('message', 'Post has been updated'); } |
Now we will fix the paging. Modify index.blade.php on the $post->render() part into this :
1 |
{{ $posts->appends( Request::query() )->render() }} |
Afterwards, we will create a menu between published and trash link on the top right corner. Modify index.blade.php :
1 2 3 4 5 6 7 8 9 10 11 12 |
<div class="col-md-6" style="padding-left: 10px; padding-right: 30px; padding-top: 10px; padding-bottom: 10px; "> <div class="pull-right" style="padding:7px 0;"> <?php $links = [];?> @foreach($statusList as $key => $value) @if($value) <?php $selected = Request::get('status') == $key ? 'selected-status' : '' ?> <?php $links[] = "<a class=\"{$selected}\" href=\"?status={$key}\">" .ucwords($key) ."({$value}) </a>"?> @endif @endforeach {!! implode(' | ', $links) !!} </div> </div> |
Modify index() and restore() method inside Backend\BlogController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
public function index(Request $request) { $onlyTrashed = FALSE; if (($status = $request->get('status')) && $status == 'trash'){ $posts = Post::onlyTrashed()->with('category','author')->latest()->paginate($this->limit); $allPostCount = Post::onlyTrashed()->count(); $onlyTrashed = TRUE; } elseif($status == 'published') { $posts = Post::published()->with('category','author')->latest()->paginate($this->limit); $allPostCount = Post::published()->count(); } elseif($status == 'scheduled') { $posts = Post::scheduled()->with('category','author')->latest()->paginate($this->limit); $allPostCount = Post::scheduled()->count(); } elseif($status == 'draft') { $posts = Post::draft()->with('category','author')->latest()->paginate($this->limit); $allPostCount = Post::draft()->count(); } else{ $posts = Post::with('category','author')->latest()->paginate($this->limit); $allPostCount = Post::count(); } return view("backend.blog.index", compact('posts','allPostCount','onlyTrashed')); } public function restore($id){ $post = Post::withTrashed()->findOrFail($id); $post->restore(); return redirect()->back()->with('message', 'Your post has been restored'); } |
Let’s create those scope inside app\Post.php
1 2 3 4 5 6 7 |
public function scopeScheduled($query){ return $query->where("published_at", ">", Carbon::now()); } public function scopeDraft($query){ return $query->whereNull("published_at"); } |
Github commit.