php 63 lines · 10 steps

Building soft deletes as an Eloquent trait in Laravel

A reusable trait marks records deleted with a timestamp and hides them from queries instead of removing rows.

Explained by highlit
1<?php
2 
3namespace App\Models\Concerns;
4 
5use Illuminate\Database\Eloquent\Builder;
6use Illuminate\Support\Carbon;
7 
8trait SoftDeletable
9{
10 public static function bootSoftDeletable(): void
11 {
12 static::addGlobalScope('notTrashed', function (Builder $builder) {
13 $builder->whereNull($builder->getModel()->getQualifiedDeletedAtColumn());
14 });
15 }
16 
17 public function getDeletedAtColumn(): string
18 {
19 return defined(static::class . '::DELETED_AT') ? static::DELETED_AT : 'deleted_at';
20 }
21 
22 public function getQualifiedDeletedAtColumn(): string
23 {
24 return $this->qualifyColumn($this->getDeletedAtColumn());
25 }
26 
27 public function trashed(): bool
28 {
29 return ! is_null($this->{$this->getDeletedAtColumn()});
30 }
31 
32 public function delete(): bool
33 {
34 $column = $this->getDeletedAtColumn();
35 $this->{$column} = $this->freshTimestamp();
36 
37 return $this->save();
38 }
39 
40 public function restore(): bool
41 {
42 $this->{$this->getDeletedAtColumn()} = null;
43 
44 return $this->save();
45 }
46 
47 public function scopeWithTrashed(Builder $query): Builder
48 {
49 return $query->withoutGlobalScope('notTrashed');
50 }
51 
52 public function scopeOnlyTrashed(Builder $query): Builder
53 {
54 return $query
55 ->withoutGlobalScope('notTrashed')
56 ->whereNotNull($this->getQualifiedDeletedAtColumn());
57 }
58 
59 protected function freshTimestamp(): Carbon
60 {
61 return Carbon::now();
62 }
63}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Soft deletion means overwriting delete to set a timestamp and filtering that column out of every query.
  2. 2A global scope applies a where clause automatically so trashed rows stay hidden without touching each query.
  3. 3Making the column name configurable through a constant keeps the trait reusable across models with different schemas.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building soft deletes as an Eloquent trait in Laravel — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code