Oh Dear is the all-in-one monitoring tool for your entire website. We monitor uptime, SSL certificates, broken links, scheduled tasks and more. You'll get a notifications for us when something's wrong. All that paired with a developer friendly API and kick-ass documentation. O, and you'll also be able to create a public status page under a minute. Start monitoring using our free trial now.

A package to automatically copy deleted records to a separate table

Original – by Freek Van der Herten – 5 minute read

We've released a new package called spatie/laravel-deleted-models. When deleting a model, this package will copy over its attributes to a table called deleted_models.

You can view this package as a "Recycle bin for models".

In this post, I'd like to tell you all about it.

Using the package

Installing the package is just a simple composer install away.

composer require spatie/laravel-deleted-models

After that, you should publish and run migrations to create deleted_models table.

php artisan vendor:publish --tag="deleted-models-migrations"
php artisan migrate

With the package installed, you should use the KeepsDeletedModels trait on every Eloquent model where you want to keep the attributes when the model is deleted.

use Illuminate\Database\Eloquent\Model;
use Spatie\DeletedModels\Models\Concerns\KeepsDeletedModels;

class BlogPost extends Model
{
    use KeepsDeletedModels;
}

When you call delete on a model, its attributes will be copied to deleted_models.

$blogPost = BlogPost::find($id);

// values will be copied to the `deleted_models` table
$blogPost->delete(); 

To restore a deleted model, you can call restore and pass in the id.

// $blogPost will be restored and returned
$blogPost = Blogpost::restore($id);

You can also restore the model in memory without saving it yet in the database, allowing you to make changes before saving it.

// the `$blogPost` isn't saved yet
$blogPost = BlogPost::makeRestored($id);

// make some modifications to the blog post

// ...

$blogPost->save();

The package has more features, and the entire deleting / restoring process can be customized. Head to the documentation on GitHub to learn more.

The trade-offs compared with soft deletes

In the Laravel ecosystem, there's another popular method of keeping deleted records: soft deleting. When using soft deletes, you add a deleted_at column to each table. When that column has a value, it should be considered deleted, and you should take care that you don't select these records when performing queries in the regular operation of your app.

The big pro for soft deletes is that it's very convenient. No data needs to be copied when deleting a model. Also, when performing migrations on a table that uses soft deletes, these "deleted" records will be updated.

But there are also some significant downsides. You must always keep soft deletes in mind whenever you perform a query via Eloquent, or when performing a query directly on your database. It's very easy to forget a where clause to omit those soft deleted records.

When using soft deletes, you also lose the benefits of your database enforcing referential integrity. Put otherwise, if you soft delete a record that other records point to via a foreign key, you have to manually update those records to be soft deleted as well. With soft deletes, your database can't enforce referential integrity.

Definitely purging data from all tables that have soft deleted records involves monitoring and performing delete queries for all those tables. One simple query is usually not enough to remove all old soft-deleted data.

To undelete a soft deleted record, could be as simple as setting the deleted_at to null. In many cases, more is needed. You also have to undelete all related records, and the original deletion might have triggered side effects outside your database that you'd need to revert. In many cases, an undelete will encompass more than just updating the deleted_at.

Using a separate deleted_models table solves some, but not all, of these problems described above, but not all. It's simply another set of trade-offs. Using a deleted_models for deleted models will:

  • not require you to adapt your queries. You can just select stuff from the original table without the need for an extra where deleted_at = null
  • allow your database to still protect the referential integrity of your database. Deleting a record will delete all related records as well.
  • make it easy to permanently delete all old data. It's just performing one deletion query one the deleted_models table with where data_created < <your-treshold-date>.
  • make it harder to restore deleted data as you need to copy stuff back in (versus just setting deleted_at to null when using soft deletes)

In closing

Our spatie/laravel-deleted-models package makes it easy to copy all deleted records to one table. It's an alternative strategy for soft deletes.

Both strategies have their unique pros and cons. Make up your mind about which of the two solutions offers the best trade-offs for your project.

This package and blog post were inspired by these two blog posts:

This package isn't the first Spatie package. Please look at this extensive list of Laravel and PHP packages we've made before. I'm sure there's something there for your next project. If you want to support our open-source efforts, consider picking up one of our paid products or subscribe at Mailcoach and/or Flare.

Stay up to date with all things Laravel, PHP, and JavaScript.

You can follow me on these platforms:

On all these platforms, regularly share programming tips, and what I myself have learned in ongoing projects.

Every month I send out a newsletter containing lots of interesting stuff for the modern PHP developer.

Expect quick tips & tricks, interesting tutorials, opinions and packages. Because I work with Laravel every day there is an emphasis on that framework.

Rest assured that I will only use your email address to send you the newsletter and will not use it for any other purposes.

Comments

What are your thoughts on "A package to automatically copy deleted records to a separate table"?

Comments powered by Laravel Comments
Want to join the conversation? Log in or create an account to post a comment.