Using Soft Delete in Laravel Eloquent Models

Using Soft Delete in Laravel Eloquent Models

Laravel provides amazing features to deal with your database records and soft delete is one of them.

Today, we will be exploring how we can practically use the soft delete functionality. Before diving into this topic, let me explain what the heck is soft delete.

According to Wikipedia:

An operation in which a flag is used to mark data as unusable, without erasing the data itself from the database.

Above definition is simple enough to understand that when models are soft deleted, they are not actually removed from your database. Instead, a deleted_at attribute is set on the model and inserted into the database. When you will look into the database table. So any model has a non-null deleted_at value will be considered as soft deleted model.

Creating Laravel Application

For the purpose of some practical examples, let’s create a new Laravel application using composer. Open your command line terminal and run the below command.

composer create-project laravel/laravel SoftDeleteApp

Once the application installation finish, create a new database in your phpMyAdmin or any MySQL client you are using. Now update the database credentials in the .env file and you are ready to go.

Creating Models and Migration

The first thing we will do in this newly created application, create a model and migration. For this example I will be creating a Post model.

Open the terminal and run the below command.

php artisan make:model Post -mf

The -mf flag will create a migration and factory for the Post model.

Once the model, migration, and factory are generated, open the migration file from the database/migrations folder and update with the below one.

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

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

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

This is a very basic migration file, with a post name and content fields. One more thing we will be adding is the deleted_at field using the $this->softDeletes().

Now, open the terminal again and run php artisan migrate command to create the posts table.

Before jumping to the next section, let’s update the Post model. Open the app\Post.php file and update with the below ones.

namespace App;

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

class Post extends Model
{
    use SoftDeletes;

    protected $fillable = ['name', 'content'];

    protected $dates = ['deleted_at'];

    protected $hidden = ['created_at', 'updated_at'];
}

In the above example, we have included the SoftDeletes trait provided by the Laravel and added to our model.

Seeding Dummy Data

Now we have our model file updated, let’s open the Laravel Model Factory for Post model which we created alongside the migration and model.

Open the PostFactory.php file from database/factories folder and update with the below ones.

use App\Post;
use Faker\Generator as Faker;

$factory->define(Post::class, function (Faker $faker) {
    return [
        'name'  =>  $faker->sentence,
        'content'   =>  $faker->realText(50)
    ];
});

Using Laravel Tinker

Instead of creating some routes with a controller, I will be using the Laravel Tinker in this post because we just want to play around with some data.

Open your command line terminal and run the below command.

php artisan tinker

Once you run the above command you will be entered into a terminal-based shell environment where we can run Laravel code.

Type the below one line of code in this terminal and hit enter.

factory('App\Post',4)->create();

Once you run the above code, Laravel’s model factory will create 4 database records for the posts table and return output something like below.

>>> factory('App\Post',4)->create();
=> Illuminate\Database\Eloquent\Collection {#2989
     all: [
       App\Post {#2985
         name: "Debitis tempora nihil aut repellat cupiditate aut dolorum.",
         content: "Dormouse went on, looking anxiously round to see.",
         id: 5,
       },
       App\Post {#2983
         name: "Laudantium aliquid voluptas laborum quis doloremque aut.",
         content: "Alice's first thought was that you think you're.",
         id: 6,
       },
       App\Post {#2982
         name: "Deleniti repudiandae dolores impedit deserunt sit.",
         content: "WOULD not remember ever having seen in her face.",
         id: 7,
       },
       App\Post {#2984
         name: "Dolor harum ad hic aut et veniam.",
         content: "The Dormouse had closed its eyes by this time.",
         id: 8,
       },
     ],
   }
>>>

Now type below code and you will be presented with the total number of records.

>>> App\Post::count();
=> 4

Now, I assume you will be familiar with this command-line utility, let’s move ahead a see how we can use the soft delete on our Post model.

Soft Deleted Models Query Operations

As we know that when we will delete a model record, it will be soft-deleted as we have used the SoftDeletes trait in our model. So let’s try to delete a record and see the output.

>>> $post = App\Post::find(2);
=> App\Post {#3003
     id: 2,
     name: "Pariatur ut id voluptatem explicabo.",
     content: "CAN I have to ask his neighbour to tell its age.",
     deleted_at: null,
   }
>>> $post->delete();
=> true
>>> $post
=> App\Post {#3003
     id: 2,
     name: "Pariatur ut id voluptatem explicabo.",
     content: "CAN I have to ask his neighbour to tell its age.",
     deleted_at: "2019-08-01 14:32:52",
   }
>>>

As you can see we firstly find the post with id 2 and then perform the delete() method on it which returned as true. When we will print this post, we can see that deleted_at field has been updated with the time when we deleted this post.

Now, if we count the number of records for our posts table, it will return the 3 records as one record is soft deleted but still exists in our database.

>>> App\Post::count();
=> 3

Including Soft Deleted Models

We can include the soft deleted models forcefully using the withTrashed() scope provided by the Laravel’s Eloquent.

>>> App\Post::withTrashed()->get();
=> Illuminate\Database\Eloquent\Collection {#2986
     all: [
       App\Post {#3004
         id: 1,
         name: "Aut harum voluptatum ipsam dolor velit fugit.",
         content: "Queen's hedgehog just now, only it ran away when.",
         deleted_at: null,
       },
       App\Post {#3007
         id: 2,
         name: "Pariatur ut id voluptatem explicabo.",
         content: "CAN I have to ask his neighbour to tell its age.",
         deleted_at: "2019-08-01 14:32:52",
       },
       App\Post {#3008
         id: 3,
         name: "Praesentium et quia expedita cum dicta dolorem.",
         content: "The hedgehog was engaged in a solemn tone, only.",
         deleted_at: null,
       },
       App\Post {#3009
         id: 4,
         name: "Veniam non est culpa.",
         content: "ONE respectable person!' Soon her eye fell upon.",
         deleted_at: null,
       },
     ],
   }
>>>

Including Only Soft Deleted Models

We can also query only the soft deleted models using onlyTrashed() method.

>>> App\Post::onlyTrashed()->get();
=> Illuminate\Database\Eloquent\Collection {#2954
     all: [
       App\Post {#2983
         id: 2,
         name: "Pariatur ut id voluptatem explicabo.",
         content: "CAN I have to ask his neighbour to tell its age.",
         deleted_at: "2019-08-01 14:32:52",
       },
     ],
   }
>>>

Including Non Soft Deleted Models

We can also fetch the records without soft deleted ones using withoutTrashed() scope.

>>> App\Post::withoutTrashed()->get();
=> Illuminate\Database\Eloquent\Collection {#2984
     all: [
       App\Post {#2996
         id: 1,
         name: "Aut harum voluptatum ipsam dolor velit fugit.",
         content: "Queen's hedgehog just now, only it ran away when.",
         deleted_at: null,
       },
       App\Post {#2990
         id: 3,
         name: "Praesentium et quia expedita cum dicta dolorem.",
         content: "The hedgehog was engaged in a solemn tone, only.",
         deleted_at: null,
       },
       App\Post {#2995
         id: 4,
         name: "Veniam non est culpa.",
         content: "ONE respectable person!' Soon her eye fell upon.",
         deleted_at: null,
       },
     ],
   }
>>>
You can chain all eloquent methods after the soft deleted methods.

Restoring Soft Deleted Models

We can also bring the soft deleted records into original state using the restore() method.

>>> $post = App\Post::withTrashed()->whereId(2)->restore();
=> 1
>>> App\Post::all();
=> Illuminate\Database\Eloquent\Collection {#2984
     all: [
       App\Post {#3011
         id: 1,
         name: "Aut harum voluptatum ipsam dolor velit fugit.",
         content: "Queen's hedgehog just now, only it ran away when.",
         deleted_at: null,
       },
       App\Post {#3010
         id: 2,
         name: "Pariatur ut id voluptatem explicabo.",
         content: "CAN I have to ask his neighbour to tell its age.",
         deleted_at: null,
       },
       App\Post {#3024
         id: 3,
         name: "Praesentium et quia expedita cum dicta dolorem.",
         content: "The hedgehog was engaged in a solemn tone, only.",
         deleted_at: null,
       },
       App\Post {#3027
         id: 4,
         name: "Veniam non est culpa.",
         content: "ONE respectable person!' Soon her eye fell upon.",
         deleted_at: null,
       },
     ],
   }
>>>

Permanently Deleting Models

Now we will look at how we can delete a record permanently using the forceDelete() method.

>>> App\Post::withTrashed()->get();
=> Illuminate\Database\Eloquent\Collection {#2990
     all: [
       App\Post {#3022
         id: 1,
         name: "Aut harum voluptatum ipsam dolor velit fugit.",
         content: "Queen's hedgehog just now, only it ran away when.",
         deleted_at: null,
       },
       App\Post {#3028
         id: 2,
         name: "Pariatur ut id voluptatem explicabo.",
         content: "CAN I have to ask his neighbour to tell its age.",
         deleted_at: "2019-08-01 14:49:41",
       },
       App\Post {#3029
         id: 3,
         name: "Praesentium et quia expedita cum dicta dolorem.",
         content: "The hedgehog was engaged in a solemn tone, only.",
         deleted_at: null,
       },
       App\Post {#3030
         id: 4,
         name: "Veniam non est culpa.",
         content: "ONE respectable person!' Soon her eye fell upon.",
         deleted_at: null,
       },
     ],
   }
>>> App\Post::onlyTrashed()->forceDelete();

Now we will query the all records like ::all().

>>> App\Post::all();
=> Illuminate\Database\Eloquent\Collection {#3031
     all: [
       App\Post {#3032
         id: 1,
         name: "Aut harum voluptatum ipsam dolor velit fugit.",
         content: "Queen's hedgehog just now, only it ran away when.",
         deleted_at: null,
       },
       App\Post {#3033
         id: 3,
         name: "Praesentium et quia expedita cum dicta dolorem.",
         content: "The hedgehog was engaged in a solemn tone, only.",
         deleted_at: null,
       },
       App\Post {#3034
         id: 4,
         name: "Veniam non est culpa.",
         content: "ONE respectable person!' Soon her eye fell upon.",
         deleted_at: null,
       },
     ],
   }
>>>

As you can see, only three records left in the database as the post with id 2 deleted permanently using the forceDelete() method.

Conclusion

In this post, we learned how we can soft delete records and query them easily. If you have any question or comments, please let us know.

Your little help will keep this site alive and help us to produce quality content for you.