Soft Deleting a Model in Laravel

Soft deleting a model in Laravel is fairly straightfoward.

You add a deleted_at column to your table using a helper method in your migration:

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

Then you add the SoftDeletes trait to your model:

class Post extends Model
{
    use SoftDeletes;
...
}

That's it. When you call Post::destroy($id), it will "soft delete" your record by adding the date/time to the deleted_at column and excluding it from subsequent query results.

But what if you want to update another column too? For example, you may want to maintain a record of the logged in user in a deleted_by column. You can do this by hooking into the deleting model event (there is one caveat though).

In the example above, you would add a boot() method to the Post model and hook into the deleting model event. Here, you update the model to add the logged in user's id to the deleted_by column.

public static function boot()
{
    parent::boot();

    static::deleting(function ($post) {
        $post->deleted_by = Auth::id();
    });
}

The Caveat

When you check your table, you'll find that the deleted_by column is not populated at all! It's because the SoftDeletes trait simply updates the deleted_at column ignoring any other changes in the model itself.

In Illuminate\Database\Eloquent\SoftDeletes:

/**
 * Perform the actual delete query on this model instance.
 *
 * @return void
 */
protected function runSoftDelete()
{
    $query = $this->newQueryWithoutScopes()->where($this->getKeyName(), $this->getKey());

    $this->{$this->getDeletedAtColumn()} = $time = $this->freshTimestamp();

    $query->update([$this->getDeletedAtColumn() => $this->fromDateTime($time)]);
}

This was a bit confusing at first because when you normally hook into event like updating or creating, the changes to the model are applied when it is saved to the database.

In this case though, you have to explicitly call the save() method on the model to persist it before the soft deletion occurs. The boot() method above would look like this:

public static function boot()
{
    parent::boot();

    static::deleting(function ($post) {
        $post->deleted_by = Auth::id();
        $post->save();
    });
}

This now works. The deleted_by column is populated with the current user's id in the database and then soft deleted.

Note: This will perform an additional UPDATE query on the database. In most cases this is fine and better than overriding a framework's method.


Liked this article? Share it on X