Laravel Soft Delete: The Ultimate Guide You Need To Read

Laravel Soft Delete: The Ultimate Guide You Need To Read

Introduction

In this article, we will discuss everything about Laravel soft delete, what is it, and how to use it, but first, let me talk to you a little bit about a feature we all know, love, and use on our computers (if you want to pass this introduction, jump to the title Laravel Soft Delete).

When you delete a file on your computer, it will be gone but you still can find it in the recycle bin and of course, you can restore it.

So basically the deleted file is not actually removed from your computer, it’s invisible and marked as Deleted, and recycle bin is just a place that shows a list of files that are marked as Deleted.

Deleting a record in your application from the database is definitive, it’s gone forever, you can’t access it again and you can’t restore it, maybe you can use a backup but I think you got the idea.

What if a user accidentally deleted some data?

What if you want to give the user the ability to restore deleted data?

What if you want to keep users for a while after they deleted their accounts?

Soft delete is the solution, it implements exactly the same logic as the recycle bin from computers, so deleting records will be just marking them as deleted and not removing them from the database.

Laravel soft delete

The Laravel team had done great work providing a built-in feature to soft delete records, it’s implementation is as easy as just adding a new table column deleted_at and using a trait SoftDeletes on your model.

How to use soft delete in Laravel

We will see now how we can implement Laravel soft delete, it’s so easy, I promise.

For example, we have a Post model and we want to add the ability to restore deleted posts, let’s enable soft delete for this model.

1. Soft delete trait: SoftDeletes

The first step is to add the SoftDeletes trait to the model:

Illuminate\Database\Eloquent\SoftDeletes

<?php

namespace App\Models;

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

class Post extends Model
{
    use SoftDeletes;
}

2. Soft delete migration

As a second and last step, we have to add the deleted_at column to the posts table, we will use the softDeletes() helper method as the following:

Schema::table('posts', function (Blueprint $table) {
    $table->softDeletes();
});

That’s it!

Now, when you call the delete method, instead of removing the record from the database, the deleted_at column will be filled with the current date and time and of course, that deleted record will always be excluded from all future query results just like if it's deleted for real.

How to retrieve soft deleted records

If we want to get a list of all soft deleted posts of a user, we can do it like this:


$trashed_user_posts = Post::onlyTrashed()
                    ->where('user_id', 1)
                    ->get();

As I said above, soft deleted records will always be excluded from query results, but if we want to include them for any reason, we can use withTrashed():

$posts = Post::withTrashed()
             ->where('user_id', 1)
             ->get();

How to restore soft deleted records

Easy, use the restore() method, it will simply set the deleted_at column to null :


$post->restore();

// OR
Post::withTrashed()
    ->where('user_id', 1)
    ->restore();

// It works with relatonships too
$user -> posts() -> restore();

You can use the trashed method to check if a record has been soft deleted:


if ($post->trashed()) {
    // Your logic here
}

How to permanently delete a record If for any reason you want to permanently delete a record, you can use the forceDelete method:

$post->forceDelete();

Pruning soft deleted records

Soft deleting records for a long time will make your database full of unnecessary data, so it makes sense to periodically delete some of the soft deleted data, to do that you can use the Illuminate\Database\Eloquent\Prunable or Illuminate\Database\Eloquent\MassPrunable trait, the difference between the two is that the MassPrunable trait will delete records using mass-deletion queries, so the model events will not be dispatched.

After choosing and using one of the traits from above, add a prunable method that returns an Eloquent query builder which determines which records you would like to prune:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Prunable;

class Post extends Model
{
    use Prunable;

    /**
     * Get the prunable model query.
     *
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function prunable()
    {
        return static::where('created_at', '<=', now()->subMonth());
    }
}

The last step is to schedule the model:prune in the App\Console\Kernel class:

/**
 * Define the application's command schedule.
 *
 * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
 * @return void
 */
protected function schedule(Schedule $schedule)
{
    $schedule->command('model:prune')->daily();
}

And you’re done.

If you want, for example, to delete the images associated with a post after being pruned, you can use the pruning method that is called just before the record is deleted:

/**
 * Prepare the model for pruning.
 *
 * @return void
 */
protected function pruning()
{
    // Your logic here
}

Note: this method will not be invoked if you used the Illuminate\Database\Eloquent\MassPrunable trait.