カルキチブログ

普通の削除と違う論理削除とは?|Laravelでデータの論理削除を行う方法について解説

お久しぶりです。カルキチ副島です。

ここ最近は業務の内容がガラッと変わったので、とても忙くてなってしまい、Twitter・ブログ共に更新頻度が下がっています。

今まではWordPress系のタスクが中心だったのですが、最近はLaravelを使った社内ツールの開発がメインのタスクになっています。

Laravel自体は使ったことはあったのですが、業務で触るのはまあ初めてのことで、、、

TODOのちょっと発展形作れますくらいのスキルしか持っていない僕のレベルでは、かなり苦労するようなタスクも多いです。

久しぶりの、今回はLaravelでデータの論理削除を行う方法について解説します。

そもそも論理削除とは?

上司の方から「データを消しちゃって元に戻したいとかでてくるだろうから、このテーブルは論理削除できるようにしておいて!」と言われたのですが、その時の僕は論理削除??!?みたいな感じになってしまいました。

→その場では僕は「承知っす!」みたいな感じで流しましたが、会話が終わった後に秒で調べました。

論理削除とは簡単に説明すると、表面上はデータが消えた様に見えるんだけど、実はデータは消さずに残してるよという状態を可能にする削除方法です。

一般的な削除(物理削除と呼ばれることが多い)は、データを完全に消し去るので、間違って消してしまった場合復旧は不可能ですが、論理削除の場合は、データの大元は残っているので、ユーザーが間違ってデータを消してしまっても元に戻すことが簡単です!

データが実際に消えていないのに消えた様に扱える原理

では、実際にデータが消えていないのに、データが消えた様に扱えるのは何故でしょうか?

論理削除では、論理削除が行われたかどうかを判定するためのカラムに追加して、対象レコードのフラグがオンなのか、オフなのかを判断することによって、データを消えた様に扱うことができます。

赤枠のdeleted_atが論理削除が行われたかどうかを判定するためのフラグです。

データがインサートされた時はdeleted_atにはNULLが入っていますが、論理削除を行う際はデータを実際に消すのではなく、deleted_atに論理削除が行われた時間を入れて更新することで、データが消えた様に扱うことができるという感じです。

まとめるとこんな感じになります。

  • 論理削除を判定するためのフラグがOFF・・・通常のデータとして扱う
  • 論理削除を判定するためのフラグがON・・・論理削除が行われたと判定

論理削除のメリット・デメリット

論理削除を行うメリットですが、データの復旧が容易!これに尽きると思います。

間違って消したら問題になりそうなデータを保存する可能性があるテーブルは論理削除できる様にしておけば、万が一データを間違って消してしまっても簡単に復旧することができます。

逆にデメリットですが、実際にデータを削除するわけではないので、データ量が増大しやすくなるというデメリットがあります。

他にも、where句に論理削除のフラグをつけ忘れてバグの原因になったりするというデメリットもあるそうですが、フレームワークの場合はそこらへんの対策はされているので、あまり心配しなくても良さそうな気はします。

実際にLaravelで試してみたのですが、データを絞り込んで取得しようとしてもNULLが返ってきました。

Railsでも対策されてそうです!

https://qiita.com/mailok1212/items/c5a1682e4f02eb0aea71

Laravelでデータの論理削除をできるようにする流れ

では、今回は僕が業務で使用しているLaravelで論理削除を行う流れを解説していきます。

テーブルとモデルの作成

まずは適当なテーブルを作ります。

php artisan make:migration create_soft_delete_samples_table --create=soft_delete_samples

適当なテーブルを作ったらマイグレーションファイルを作ります。

    public function up()
    {
        Schema::create('soft_delete_samples', function (Blueprint $table) {
            $table->id();
            $table->string('book');

            // SoftDeleteを行うために必要なカラムを追加する
            $table->softDeletes();
            $table->timestamps();
        });
    }

$table->softDeletes();という行を追加することで、deleted_atというTIMESTAMP型のカラムが追加されます。

フレームワークはこの辺は便利です。

マイグレーションを実行して、テーブルができたことを確認できたら次はモデルを作ります。

論理削除を行うためには、モデルにも一手間加える必要があります。

→僕は初見では、ここが分からずそのままプルリクを出して「出来てないじゃん」って突っ込まれました。

<?php

namespace app;

use illuminate\database\eloquent\model;
use illuminate\database\eloquent\softdeletes;

class softdeletesample extends model
{
  // 論理削除を使用できるようにする
  use softdeletes;

  // 論理削除された際は以下で指定しているdeleted_atカラムに日付が設定される
  // 5.7以降は不要
  protected $dates = ['deleted_at'];

  protected $fillable = ['book'];
}

バージョン5.7以降はuse SoftDeletes;と記載するだけで論理削除をできる様になるそうです。

この記事を書いている時に知りました。

https://readouble.com/laravel/7.x/ja/eloquent.html

公式を読むのはやっぱ大事ですね。

実際に論理削除してみる

では、実際に論理削除をやってみます。

念のために補足しておきますと、データの挿入などの処理は本来はコントローラーのメソッド内に書かないとダメだし、insertも複数代入のリスクやcreated_atやupdated_atにデータを指定してあげないとNULLになったりなど、デメリットが多いのでデータを挿入する際は基本使わないです。

ただ、今回はあくまでサンプルなので、アクセスした時にデータが挿入される様にしてみます。

use App\SoftDeleteSample;
use Carbon\Carbon;

Route::get('/', function () {
    $sample_records = [
      [ 'book' => '鬼滅の刃', 'created_at' => Carbon::now()],
      [ 'book' => 'ドラベース', 'created_at' => Carbon::now()],
      [ 'book' => '人体図鑑', 'created_at' => Carbon::now()],
    ];
    SoftDeleteSample::insert($sample_records);

    return view('welcome');
});

Route::get('/sample', function () {
    $soft_delete_samples = SoftDeleteSample::all();

    return view('test', compact('soft_delete_samples'));
});

鬼滅の刃・ドラベース好きな人は仲良くなりましょう。

話が若干外れましたが、次は適当なページにDBに保存されたデータを表示しましょう。

test.blade.phpは、こんな感じです。

<div>
  @foreach ($soft_delete_samples as $soft_delete_sample)
    <div>
      <span>{{ $soft_delete_sample->id }}</span>
      <span>{{ $soft_delete_sample->book }}</span>
      <span>{{ $soft_delete_sample->created_at->format('Y-m-d') }}</span>
    </div>
  @endforeach
</div>

ブラウザで確認してみましょう。

表示できました。

では、実際に論理削除できたかどうか確認するためにデータを削除してみましょう。

人体図鑑を削除してみます。

Route::get('/sample-delete', function () {
    SoftDeleteSample::find(3)->delete();
    return view('test_delete');
});

/sample-deleteにアクセスしてレコードを削除してから、/sampleに再度アクセスしてみてデータが消えたかどうか確認してみてください。

人体図鑑が表示されなくなりました。が、、、

消してから何日か経って、「消しちゃったけど、やっぱ、人体図鑑読みたいわ・・・」ってなってしまいました。

物理削除の場合は、完全にデータを消し去るので無理ですが、論理削除の場合はというと、、、

データは消えていません!!

データは消えずに、該当レコード内のdeleted_atのみ更新されたことが分かるかと思います。

Laravelの場合は、onlyTrashed()メソッドというソフトデリート済みのモデルのみを取得するメソッドを使えば簡単にデータを元に戻すことができます。

Route::get('/sample-delete', function () {
    SoftDeleteSample::onlyTrashed()->find(3)->restore();
    return view('test_delete');
});

/sample-deleteにアクセスしてから、/sampleにアクセスして、消したデータが復活していることを確認してみてください。

Laravelでは、ソフトデリートされたデータも含めて結果を取得したり、論理削除されたデータを完全に削除したりもできるそうなので、興味がある方は調べてみてください。

まとめ

長かったですが、まとめです。

  • 物理削除・・・完全にデータを消し去る一般的な削除。復旧できない。
  • 論理削除・・・消えた様に見せかけるだけで、実際に消してはいない。復旧は容易だが、データを消すわけではないので、データはどんどん溜まっていく。

まあ、極論これだけなんですけどね!

テーブル設計を行う際は、間違って消してしまったら困るデータを保存する可能性があるテーブルに関しては、論理削除できる様にしてあげると、後々のことを考えるといいかもしれないです!

おまけ

久しぶりにブログを更新したわけですが、ブログの方もちょっとアップデートしました。

超地味な変化なのですが、カテゴリーとタグページにもページネーションをつけました。

改修点としては割と地味なのですが、結構難しかったので、後々記事を書けたらなと思ってます。