Working With Soft Deletes in Laravel 8 and 9 (By Example)
The annoying thing about deleting data from a database is that it’s gone forever. We can’t even look at the data to see if we needed it because it’s gone. If we need the data back, our only solution will be to restore a backup and hope we have the backup just before it was deleted so the loss is minimized.
Thankfully, Laravel provides a built-in feature that allows us to flag database rows as deleted without actually deleting them from the database. This article/video discusses how to use Soft Deletes in Laravel.
Why Should We Use Soft Deletes?
As we’ve discussed in “Stop Deleting Data”, when we DELETE
a row from the database it’s gone forever without going through a potentially painful process of doing a full database restore. Soft deleting the data allows us to easily view and restore the data with minimal work and can be a huge time saver when data is accidentally deleted.
Laravel provides support for soft deleting using the Illuminate\Database\Eloquent\SoftDeletes
trait.
Migrations
The first part of the process we need to tackle is setting up our database tables to have the SoftDeletes
column.
Adding the Soft Delete Columns to a New Table
Let’s start by creating a new model to track a Project
in a project management application. We’ll create the model and migration in one step so we can be as lazy as possible.
When we open the newly created migration we’ll add the following lines to our up()
function.
It looks like this.
The $table->softDeletes();
function call is what sets up the table to allow for the SoftDeletes
trait to work. Without it, we’ll get query errors.
Now we’ll run the migration.
Let’s look at what the table looks like inside MySQL.
As we can see there’s a deleted_at
column in the table definition. This is what the $table->softDeletes();
call added to the table and what the SoftDeletes
trait will use to track if the row has been deleted.
Adding the Soft Delete Columns to an Existing Table
Let’s look at how we can add the soft delete columns to an existing table. We’re going to create a new migration using the make:migration
command to add the columns to the users
table because out of the box Laravel doesn’t have this column on the users
table.
Next we’re going to alter the migration so it both adds the columns in the up()
function ($table->softDeletes();
) and removes them in the down()
function ($table->dropSoftDeletes();
).
Again, we’ll run the migration.
Setting Up the Model to Use Soft Deletes
Now that we have our database tables set up we can start working with soft deleted models in our code. The first step is adding the Illuminate\Database\Eloquent\SoftDeletes
trait to the models. Below is an example model where we have set it up to use the SoftDeletes
logic.
It’s important to note that even though we added the SoftDeletes
column to our model Laravel doesn’t automatically use it until we add the trait so we will still irreparably delete data without it.
Deleting a Model
Now that everything is set up let’s test it to see what happens.
First we’ll use tinker to create a new Project
.
When we check the database we can see that it’s been persisted to the database and the deleted_at
column is set to null
indicating that it hasn’t been deleted.
Now we’ll delete the model.
And back in MySQL, we can see deleted_at
is no longer null
which indicates it has been deleted.
Restoring a Model
If we accidentally delete the model Laravel makes it easy to restore the record using the restore()
function.
Deleting a Model
Let’s say you have a case where someone accidentally entered information and we need to delete the record from the database and not just mark it as deleted. SoftDeletes
provides the forceDelete()
function that will do just that.
Finding a Deleted Model
What happens when we delete a model and need to find it later?
First, let’s set up a new Project
and soft delete it.
When we attempt to use findOrFail()
we’ll receive a ModelNotFoundException
because the SoftDeletes
trait is filtering them out.
To get around this we need to call the withTrashed()
function before we call findOrFail()
.
Finding a Deleted Model In a Relationship
The other part of our code we need to be on the lookout for is when we have a model that defines an Eloquent relationship with the SoftDeletes
models.
Normally, we would define our relationship like so.
If the User
associated with this Project
is soft-deleted and we attempt to access it through this relationship the function will return null
.
The solution to this again is to use the withTrashed()
function to have it return a result.
Viewing Trashed Models From a Controller
One of the annoying parts about the soft delete trait is that if a user attempts to access a page for a deleted resource they’ll receive a 404 error and not something helpful like a message informing them the item was deleted. To get around this we can add a call to withTrashed()
to our route definition.
Adding deleted_by
As a small aside, one of the things we like to do is also track who deleted the model. This is helpful because then not only can we tell people when an entity was deleted but can also tell them who did it. It makes it so much easier to track down why it was deleted it if wasn’t supposed to be.
We do this by adding a deleted_by
column and then we create our own delete()
function that sets both the deleted_at
and deleted_by
columns to the correct values and saves the results.
We generally add this to our own SoftDeletes
trait or class.
What You Need To Know
- Soft deletes allow us to keep data in the database
- Makes it easier to restore
- Makes it easier to see when it was deleted
Scott Keck-Warren
Scott is the Director of Technology at WeCare Connect where he strives to provide solutions for his customers needs. He's the father of two and can be found most weekends working on projects around the house with his loving partner.
Top Posts
- Working With Soft Deletes in Laravel (By Example)
- Fixing CMake was unable to find a build program corresponding to "Unix Makefiles"
- Upgrading to Laravel 8.x
- Get The Count of the Number of Users in an AD Group
- Multiple Vagrant VMs in One Vagrantfile
- Fixing the "this is larger than GitHub's recommended maximum file size of 50.00 MB" error
- Changing the Directory Vagrant Stores the VMs In
- Accepting Android SDK Licenses From The OSX Command Line
- Fixing the 'Target class [config] does not exist' Error
- Using Rectangle to Manage MacOS Windows