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.