LaravelでDBのデータを削除する時に論理削除する方法について
- 物理削除と論理削除の違い
- なぜ論理削除を採用するのか
を絡めながらまとめます。
環境
$ composer -V
Composer version 2.0.14 2021-05-21 17:03:37
$ php artisan --version
Laravel Framework 8.53.0
物理削除と論理削除の違い
まずは削除の中の「物理削除」と「論理削除」の違いです。
物理削除 | 論理削除 | |
---|---|---|
概要 | レコード自体を削除する | テーブルに「削除扱いするか」を判定するためのカラムを設け、そのカラムの値で削除の管理・判断を行う |
SQL | DELETE | UPDATE |
メリット | ・DELETE文を発行するため、実装が楽 ・レコードが削除される為、ストレージを圧迫しにくい | ・間違えて削除されたデータの復元が簡単 ・データを一時的に使用禁止とかにしやすい |
デメリット | ・簡単にデータを復元できない ・簡単に削除したデータを参照できない | ・削除してもレコードが減らないのでストレージを圧迫していく(=性能悪化に繋がる) ・SQLがUPDATEになるため直観的に理解しづらい ・検索処理時のWHERE句の条件を忘れがち |
言葉通りですが、物理的に削除する「物理削除」(そのままですみませんw)に対し、「論理削除」はデータはあるけど、システム上削除されている状態として扱う方法です。
なぜ論理削除を採用するのか
論理削除を採用するケースの多くはテーブルが以下の場合です。
- そのテーブルで取り扱っているデータがトランザクションデータの場合(※1)
- 削除処理するとしてもバックエンドではデータとして残しておきたいデータの場合
トランザクションデータとマスターデータをそれぞれざっくりと。
データ種類 | 概要 |
---|---|
トランザクション データ | 蓄積されず、変更があったら値が更新されていくデータ |
マスターデータ | 基本的に値は更新されず、蓄積されていくデータ |
データ管理(復元の容易さ)や、他テーブルとのリレーション状態の保持の関係から、論理削除を採用した方が良いケースは多そうですが、それなりにデメリットもあるので根拠なしに論理削除を採用するのは良くないです。
Laravelでの論理削除の方法
前置きが長くなったのですが、Laravelでの論理削除の実装方法をご紹介します。
論理削除したいテーブルにdeleted_atカラムを追加する
Laravelにデフォルトで準備してくれている仕組みとしての論理削除(ソフトデリート)ではdeleted_at
カラムの値が以下のそれぞれのケースで削除されていない/削除されているを判断します。
NULL
(デフォルト):削除されていないTIMESTAMP
型の値:削除されている
マイグレーションファイルにsoftDeletes()
を追記することで論理削除用のdeleted_at
カラムを追加することができます。
Schema::table('Users', function (Blueprint $table) {
// 略
$table->softDeletes();
});
softDeletes()
の定義は以下のとおりです。
/**
* Add a "deleted at" timestamp for the table.
*
* @param string $column
* @param int $precision
* @return \Illuminate\Database\Schema\ColumnDefinition
*/
public function softDeletes($column = 'deleted_at', $precision = 0)
{
return $this->timestamp($column, $precision)->nullable();
}
- カラム名は
deleted_at
- データの型は
TIMESTAMP
型 - NULL許容
のカラムを追加しているのがわかります。
なのでそのままtimestamp()
を使って定義しても問題ないです。
Schema::table('Users', function (Blueprint $table) {
// 略
$table->timestamp($column, $precision)->nullable();
});
ただ、softDeletes()
を使った方がコード量が少ないしパッとみで論理削除することがわかる(softって書いている)のでsoftDeletes()
を使う方が良いのかなと思いました。
本題とはズレますが、timestamp()
の第二引数の$precision
はタイムスタンプの数字の細かさ(精度)を設定することができるようです。

論理削除したいテーブルのモデルでSoftDeletesトレイトを使う
論理削除したいテーブルに対応するモデルファイルに以下のとおりuse SoftDeletes;
を追記すればOKです。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class User extends Model
{
// 追記
use SoftDeletes;
}
これだけです。
モデルでdeleteメソッドで削除を行う
ここまで終わったらあとはdelete()
で特定のモデルインスタンスを削除すれば論理削除ができます。
かなりざっくりですが、モデルでuseしたSoftDeletes
トレイトの削除時の処理を見てみます。
Model.php
のdelete
メソッドでこのトレイトのperformDeleteOnModel
メソッドが呼ばれる
// 略
public function delete()
{
// 略
$this->performDeleteOnModel();
// 略
}
performDeleteOnModel
メソッドからrunSoftDelete
が呼ばれる
// 略
protected function performDeleteOnModel()
{
// 略
return $this->runSoftDelete();
}
runSoftDelete
メソッドで論理削除を実行する
// 略
protected function runSoftDelete()
{
$query = $this->setKeysForSaveQuery($this->newModelQuery());
$time = $this->freshTimestamp();
$columns = [$this->getDeletedAtColumn() => $this->fromDateTime($time)];
$this->{$this->getDeletedAtColumn()} = $time;
if ($this->timestamps && ! is_null($this->getUpdatedAtColumn())) {
$this->{$this->getUpdatedAtColumn()} = $time;
$columns[$this->getUpdatedAtColumn()] = $this->fromDateTime($time);
}
$query->update($columns);
$this->syncOriginalAttributes(array_keys($columns));
}
runSoftDelete
メソッドの処理内容はざっくりこんな感じだと思います。
deleted_at
カラムに現在日時をセットする$query->update($columns);
でSQLのUPDATE
を発行する
補足
Laravelで用意された論理削除機能を使うと、Eloquent(ORM)でモデルを通してDBからデータを取得する際に取得条件に削除済(deleted_atがnullではない)のモデルが含まれていても自動的に除外してくれます。
とても便利です。
公式ドキュメントにもこの記載
ソフトデリートされたモデルに対しクエリがあっても、削除済みのモデルはクエリ結果に含まれません。
最後に
この記事のまとめです。
- なんでもかんでも論理削除にすれば良いっていうわけではない(逆も然り)
- 論理削除を採用すべきかの感覚と経験が大事
- フレームワークでの論理削除は簡単すぎてビビる
- でもソースコードをちゃんと追えば処理内容は理解できる
- データ取得時のよしなに感が最高
Laravel学習にオススメの書籍や教材はこちらにまとめています。

コメント