Soft delete models in Laravel: the definitive guide

Soft delete models in Laravel: the definitive guide

Modified
Dec 21, 2023
Written by
Benjamin Crozat
0
comments
3 minutes
read

What are soft deletes?

Soft deletes allows developers to mark models as deleted without deleting them from the database.

Imagine a deleted_at column in your database containing the date where your entry has been deleted.

Their main benefit is that you don’t loose data anymore. You will always be able to restore it.

Obviously, there is a mechanism to help you clean up old soft deleted models that I’ll show you later.

How to set up soft deletes

Laravel requires you to take two easy steps to set up a soft delete.

First, specify that you need a column for soft deletion in your migration (I wrote a nice article about migrations). Once you run it, you’ll see a new deleted_at column in your posts table.

public function up()
{
	Schema::table('posts', function (Blueprint $table) {
		$table->softDeletes(); // [tl! ++]
	});
}

In the down() method, you can remove the column using the dropSoftDeletes() method.

public function down()
{
    Schema::table('posts', function (Blueprint $table) {
		$table->dropSoftDeletes(); // [tl! ++]
	});
}

Then, in your model, import the SoftDeletes trait.

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes; // [tl! ++]
use Illuminate\Database\Eloquent\Factories\HasFactory;

class Post extends Model
{
    use HasFactory, SoftDeletes; // [tl! ++]
}

How to perform a soft delete and check for it

To soft delete in Laravel, you don’t have to change your habits. Use your model’s delete() method just like before. The only difference will be that Laravel will add the current date and time inside the deleted_at column.

$post->delete();

And if you want to check if a model has been soft deleted, I recommend the trashed() method instead of manually checking the deleted_at column.

if ($post->trashed()) {
    //
}

More helpers for soft deletes

Sometimes, you may need to include soft deleted models in your queries. The withTrashed() scope can help with that.

Post::withTrashed()->get();

You can even query trashed models only:

Post::onlyTrashed()->get();

Also, since the model is never really deleted, you can restore it at any moment using the restore() method. The deleted_at column will be back to NULL.

$post->restore();

Finally, if you want to really remove a soft deletable model from the database definitely, use the forceDelete() method.

$post->forceDelete();

How to test for a soft delete

To test for a soft delete in Laravel, use the assertSoftDeleted() method Laravel provides.

Here’s a basic example of how I’d go for it:

public function test_it_soft_deletes_posts()
{
    $post = Post::factory()->create();
    
    $this
        ->deleteJson(route('posts.destroy', $post))
        ->assertNoContent();
        
    $this->assertSoftDeleted($post);
}

The inverse method exists as assertNotSoftDeleted().

How to clean up old soft deleted models

You can use the pruning mechanism Laravel offers to clean up old soft deleted models.

For example, import the Prunable trait inside your model and tell the framework to remove models that have been soft deleted since a month or more.

namespace App\Models;

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

class Post extends Model
{
    use HasFactory, Prunable, SoftDeletes;
  
    public function prunable()
	{
		return static::where('deleted_at', '<=', now()->subMonth());
	}
}

Don’t forget to run the model:prune command with the scheduler. Add it to your app/Console/Kernel.php:

protected function schedule(Schedule $schedule)
{
    $schedule->command('model:prune')->daily(); // [tl! ++]
}
About Benjamin Crozat
Benjamin Crozat

Hi! I’m from the South of France and I’ve been a self-taught web developer since 2006. When I started learning PHP and JavaScript, PHP 4 was still widely used, Internet Explorer 6 ruled the world, and we used DHTML to add falling snow on websites.

Being able to educate myself for free on the web changed my life for the better. Giving back to the community was a natural direction in my career and I truly enjoy it.

Therefore, I decided to take action:

  1. I launched this blog in September 2022 with the goal to be in everyone’s Google search. I get more than tens of thousands of monthly clicks from it and even more visits overall (my analytics dashboard is public by the way).
  2. I also started growing my X (formerly Twitter) account at the same time, which has now over 7,000 followers.
  3. All the content I write is free thanks to my sponsors.

I also want to be completely free with my time and make a living with my own products. In April 2024, I launched Nobinge, a tool to summarize and chat with your content, including YouTube videos.

Believe me, I’m just getting started!

0 comments

You need to be signed in to comment this post.
Sign in with GitHub