🚀

【Laravel6】論理削除が簡単すぎてビビったけどしっかりと理解していく

5 min read

概要

Laravel6でコンテンツを削除する時に論理削除する方法を調べたら簡単すぎてビビったので

  • 物理削除と論理削除の違い
  • なぜ論理削除を採用するのか
  • Laravel6での論理削除の方法

をまとめます。

環境

$ composer -V
Composer version 1.10.20 2021-01-27 15:41:06

$ php artisan --version
Laravel Framework 6.20.16

物理削除と論理削除の違い

物理削除 論理削除
概要 レコード自体を削除する テーブルに削除フラグのカラムを設け、
そのTRUE or FALSEで削除の管理・判断を行う
SQL DELETE UPDATE
メリット ・DELETE文を発行するため、実装が楽
・レコードが削除される為、ストレージを圧迫しにくい
・間違えて削除されたデータの復元が簡単
・データを一時的に使用禁止とかにしやすい
デメリット ・簡単にデータを復元できない
・簡単に削除したデータを参照できない
・削除してもレコードが減らないのでストレージを圧迫していく(=性能悪化に繋がる)
・SQLがUPDATEになるため直観的に理解しづらい
・検索処理時のWHERE句の条件を忘れがち

なぜ論理削除を採用するのか

論理削除を採用するケースの多くはそのテーブルが以下の場合です。

  • そのテーブルで取り扱っているデータがトランザクションデータの場合(※1)
  • 削除処理するとしてもバックエンドではデータとして残しておきたいデータの場合

(※1)マスターデータの場合でも論理削除を採用する場合は多々あります。

トランザクションデータとマスターデータをそれぞれざっくりと。

データ種類 概要
トランザクション
データ
蓄積されず、変更があったら値が更新されていくデータ
マスターデータ 基本的に値は更新されず、蓄積されていくデータ

データ管理(復元の容易さ)や、他テーブルとのリレーション状態の保持の関係から、論理削除を採用した方が良いケースは多そうですが、
それなりにデメリットもあるので根拠なしに論理削除を採用するのは良くないですね🙅‍♂️

Laravel6での論理削除の方法

前置きが長くなったのですが、Laravel6系での論理削除の実装方法をご紹介します。

結論:めっちゃ簡単です。

論理削除したいテーブルにdeleted_atカラムを追加する

Laravelにデフォルトで用意された論理削除(ソフトデリート)はdeleted_atカラムの値が以下のそれぞれのケースで削除されていない/削除されているを判断します。

  • NULL(デフォルト):削除されていない
  • TIMESTAMP型の値:削除されている

マイグレーションファイルにsoftDeletesメソッドを追記することで論理削除用のdeleted_atカラムを追加できます。

database/migrations/2021_02_11_134525_create_articles_table.php
Schema::table('Users', function (Blueprint $table) {
    // 略
    $table->softDeletes();
});

softDeletesメソッドを見てみます。

vendor/laravel/framework/src/Illuminate/Database/Schema/Blueprint.php
    /**
     * 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許容

のカラムを追加しているのがわかります。

論理削除したいテーブルのモデルでSoftDeletesトレイトを使う

テーブルのモデルに以下のとおりuse SoftDeletes;を追記すればOK

app/User.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class User extends Model
{
    use SoftDeletes;
}

めっちゃ簡単!!

モデルでdeleteメソッドで削除を行う

モデルに用意されたdeleteメソッドで特定のモデルを削除すれば論理削除ができます。

かなりざっくりですが、モデルでuseしたSoftDeletesトレイトの削除時の処理を見てみます。

1.Model.phpdeleteメソッドでこのトレイトのperformDeleteOnModelメソッドが呼ばれる

vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php
    // 略
    
    public function delete()
    {
        // 略
	
        $this->performDeleteOnModel();

        // 略
    }

2.performDeleteOnModelメソッドからrunSoftDeleteが呼ばれる

vendor/laravel/framework/src/Illuminate/Database/Eloquent/SoftDeletes.php
    // 略
    
    protected function performDeleteOnModel()
    {
        // 略
        return $this->runSoftDelete();
    }

3.runSoftDeleteメソッドで論理削除を実行する

vendor/laravel/framework/src/Illuminate/Database/Eloquent/SoftDeletes.php
    // 略
    
    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で用意された論理削除機能を使うと、ORMでモデルからデータを取得する際に取得条件に削除済のモデルが含まれていても自動的に除外してくれます。

いや...

便利すぎますやん!!

(「ハンパないって!そんなんできひんやん普通!」)

公式ドキュメントにもこの記載

ソフトデリートされたモデルに対しクエリがあっても、削除済みのモデルはクエリ結果に含まれません。

まとめ

  • なんでもかんでも論理削除にすれば良いっていうわけではない(逆も然り)
  • 論理削除を採用すべきかの感覚と経験が大事
  • フレームワークでの論理削除は簡単すぎてビビる
  • でもソースコードをちゃんと追えば処理内容は理解できる
  • データ取得時のよしなに感が最高

参考

Discussion

ログインするとコメントできます