How to use softDeletes in Laravel with example

When working in production mode, delete method should not be permanently delete records from the database. There should be backup before delete them. Someone by mistake delete the data which was required. You might want to backup deleted records for future reference.

In this situation, Laravel provides soft delete feature which never delete data from database. It just hide them from showing. You can always get deleted data and restore or permanently delete them.

In this tutorial example, I will share you how you can build a softdelete system. We will go through step by step. So let's start from creating fresh Laravel application.

Step 1: Create a new Laravel application

First step always starts from creating new Laravel application. So open the Terminal or CMD and run the following command: 

composer create-project laravel/laravel post

Change the directory to Laravel application root directory.

cd post

Step 2: Configure database

In the second step, we will set database credentials in the .env file. So import porject in your favourite text editor. Open file and change below credentials according to your MySQL database.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=post
DB_USERNAME=root
DB_PASSWORD=root

Step 3: Create migration

Now we will create database table to store data. So in the Terminal run the migration command.

php artisan make:migration create_posts_table

This will create a migration file at database/migrations directory. Open the migration file and add the table fields.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('content');
            $table->softDeletes(); // this will create deleted_at field for softdelete
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}

And run the migrate command to create table in database

php artisan migrate

Step 4: Create Model

In this step, we will create a model which will used to add SoftDeletes trait. Run the artisan command in Terminal.

php artisan make:model Post

This will create a Post model class at app/Models directory. Open model class and add the Illuminate\Database\Eloquent\SoftDeletes trait to the model. This will enable softdelete feature.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class Post extends Model
{
    use HasFactory, SoftDeletes;
}


Step 5: Create controller class

We will not implement whole CRUD operation. Instead we only implement Get records and delete feature. This will make tutorial simple and easy to understand how softdelete works. Run the following command in Terminal to create controller.

php artisan make:controller PostController

This will create a PostController class at app/Http/Controllers directory. Open controller and add the following methods in it. We will explain all methods one by one.

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index(Request $request)
    {
        if ($request->has('trashed')) {
            $posts = Post::onlyTrashed()
                ->get();
        } else {
            $posts = Post::get();
        }

        return view('posts', compact('posts'));
    }
  
    /**
     * soft delete post
     *
     * @return void
     */
    public function destroy($id)
    {
        Post::find($id)->delete();
  
        return redirect()->back();
    }
  
    /**
     * restore specific post
     *
     * @return void
     */
    public function restore($id)
    {
        Post::withTrashed()->find($id)->restore();
  
        return redirect()->back();
    }  
  
    /**
     * restore all post
     *
     * @return response()
     */
    public function restoreAll()
    {
        Post::onlyTrashed()->restore();
  
        return redirect()->back();
    }
}

Explanation of methods:

index() method views all post or soft deleted post.
destroy() method will soft delete specific post from the list
restore() method will restore specific post from list
restoreAll() method will restore all soft deleted post.

Step 6: Create routes

In this step, we need to create routes for controller. Common routes are located in routes/web.php file. So open file and add the following routes in it.

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PostController;

Route::get('posts', [PostController::class, 'index'])->name('posts.index');
Route::delete('posts/{id}', [PostController::class, 'destroy'])->name('posts.destroy');
Route::get('posts/restore/{id}', [PostController::class, 'restore'])->name('posts.restore');
Route::get('posts/restore-all', [PostController::class, 'restoreAll'])->name('posts.restoreAll');

Step 7: Create blade file

In the last step of coding, we need to create view which will display and do all the stuff. So create a blade file resources/views/posts.blade.php.

<!DOCTYPE html>
<html>
<head>
    <title>All Posts</title>
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
</head>
    <body>
        <div class="container">
            <h1 class="m-3 text-center">All Posts</h1>
            <table class="table table-bordered mb-3">
                <thead>
                    <tr>
                        <th>No</th>
                        <th>Title</th>
                        <th>Action</th>
                    </tr>
                </thead>
                <tbody>
                    @foreach($posts as $post)
                        <tr>
                            <td>{{ $post->id }}</td>
                            <td>{{ $post->title }}</td>
                            <td>
                                @if(request()->has('trashed'))
                                    <a href="{{ route('posts.restore', $post->id) }}" class="btn btn-success">Restore</a>
                                @else
                                    <form method="POST" action="{{ route('posts.destroy', $post->id) }}">
                                        @csrf
                                        <input name="_method" type="hidden" value="DELETE">
                                        <button type="submit" class="btn btn-danger delete" title='Delete'>Delete</button>
                                    </form>
                                @endif
                            </td>
                        </tr>
                    @endforeach
                </tbody>
            </table>
            <div class="float-end">
                @if(request()->has('trashed'))
                    <a href="{{ route('posts.index') }}" class="btn btn-info">View All posts</a>
                    <a href="{{ route('posts.restoreAll') }}" class="btn btn-success">Restore All</a>
                @else
                    <a href="{{ route('posts.index', ['trashed' => 'post']) }}" class="btn btn-primary">View Deleted posts</a>
                @endif
            </div>
        </div>
        <script type="text/javascript">
            $(document).ready(function() {
                $('.delete').click(function(e) {
                    if(!confirm('Are you sure you want to delete this post?')) {
                        e.preventDefault();
                    }
                });
            });
        </script>
    </body> 
</html>

If you need to create fake records, insert below query to MySQL command prompt multiple times or use Factory.

INSERT INTO `posts` (`id`, `title`, `content`, `created_at`, `updated_at`, `deleted_at`) VALUES (NULL, 'Sticky footer with fixed navbar', 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\r\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\r\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\r\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\r\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\r\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.', NULL, NULL, NULL);

Now our coding part is done. We need to start Laravel server using below command.

php artisan serve

In your browser, go to the url http://localhost:8000/posts and try to delete and restore post.

Conclusion

This way, you can backup and restore data which accidently deleted. I hope this tutorial will help you in your web development.

Tags: