Laravelでは論理削除を行う機能が備わっています。
論理削除と物理削除に関しては以下に詳しく紹介されています。
参考:論理削除と物理削除とは
データを削除したいけれど、後々データの復元を行う可能性がある場合などに用います。
会員データなどはすぐに削除を行わず、一定時間が経過後削除を行うシステムはよく見受けられます。
ただ、データが累積し検索機能の低下につながるため、よく検討し必要に応じた実装を心がけるようにしましょう。
環境
Laravel5.5
Laravel-admin1.6
MariaDB 10.1.30
この記事の実証内容と結果
削除方法としては以下を試しました。
①ORMによる削除
②DBファサードのdeleteによる削除
③DBファサードのtruncateによる削除
④クエリビルダによる削除
各削除方法で論理削除が行えているかを検証してみました。
また、削除対象のレコードは以下の2種類のレコードに対し実行しました。
以下、検証結果となります
ア.普通のレコード
「普通のレコード」とは「論理削除されていない」という意味になります。
①ORMによる削除
当たり前ながら「deleted_at」に日付が挿入され、論理削除が機能されていることが確認できました。
②~④
ORM以外の削除では、モデルに論理削除であることを宣言していても、レコードは物理削除されてしまうことが確認できました。
イ.論理削除済みのレコード
①の方法で論理削除を行ってから②~④の方法を実行しました。
当たり前の結果かもしれませんが、論理削除済みのレコードは物理削除されます。
したがって、論理削除を行いたい場合はORMを使用するか、処理速度の関係でORM以外を使用する場合はdeleteやtruncateではなく、deleted_atに値を挿入する、という選択をとる必要があります。
実証方法・内容の詳細
Laravelの論理削除実装方法はLaravel公式ページに記載されています。
方法としては論理削除を実装したいテーブルのモデルに、以下のグレーマーカーの2行の記述を追加します。
・TestUser.php
1 2 3 4 5 6 7 8 9 10 |
<?php namespace App\Admin\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; class TestUser extends Model { use SoftDeletes; |
また、対象テーブルにdeleted_atカラムが存在することが条件です。今回使用するテーブルは以下のようにしています。
「$table->softDeletes()」の記述をマイグレーションファイルに記述することでDelete_atカラムが追加されます。
・マイグレーションファイル
1 2 3 4 5 6 7 8 9 10 |
Schema::create('test_users', function (Blueprint $table) { $table->increments('id'); $table->string('name')->nullable(); $table->string('age')->nullable(); $table->string('email')->nullable(); $table->boolean('living_together')->nullable(); $table->string('family_address')->nullable(); $table->timestamps(); $table->softDeletes(); }); |
以上で完了です。
実証方法はLaravel-adminのフォーム上にボタンを設けそこから削除を行っていきます。
ア.普通のレコード
今回Laravel-adminのフォーム上に4種類のボタンを設置しました。
「復活の呪文」に関してはrestore関数を使用しています。
restore関数ではwhere文による条件指定も行えるほか、onlyTrashedなどで論理削除済みのみの取得など、様々な操作を行うことができます。
Toolクラス
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
<?php namespace App\Admin\Extensions\Tools; use Encore\Admin\Admin; use Encore\Admin\Grid; use Encore\Admin\Grid\Tools\AbstractTool; use Illuminate\Support\Facades\Request; class SoftDeletesVerification extends AbstractTool { /** * Set up script for import button. */ protected function script() { return <<< SCRIPT $('.soft_deletes_verification').unbind('click').click(function() { var type = $(this).attr("id"); console.log('test') $.ajax({ method: 'post', url: "softdeletes", data: {type:type}, headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') }, success: function (data) { $.pjax.reload('#pjax-container'); } }); }); SCRIPT; } public function render() { Admin::script($this->script()); $options = [ 'ORM' => 'orm', 'DBファサード(delete)' => 'dbfacade_delete', 'DBファサード(truncate)' => 'dbfacade_truncate', 'クエリビルダー' => 'query_builder', '復活の呪文' => 'revival_spell', ]; return view('soft_deletes_verification', compact('options')); } } |
コントローラ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?php namespace App\Admin\Controllers; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use App\Admin\Models; use Encore\Admin\Layout\Content; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; class SoftDeletesVerification extends Controller { public function softDeletesVerification(Request $request) { $deleteType = $request->input('type'); if ($deleteType == 'orm') { Models\TestUser::query()->delete(); } elseif ($deleteType == 'dbfacade_delete') { DB::table('test_users')->delete(); } elseif ($deleteType == 'dbfacade_truncate') { DB::table('test_users')->truncate(); } elseif ($deleteType == 'query_builder') { DB::delete('delete from test_users'); } else { Models\TestUser::query()->restore(); } } } |
画面はこんな感じ↓
以下が実行前のレコードです。
1 2 3 4 5 6 7 |
MariaDB [testdb]> select * from test_users; +----+-----------+------+-------+-----------------+----------------+---------------------+---------------------+------------+ | id | name | age | email | living_together | family_address | created_at | updated_at | deleted_at | +----+-----------+------+-------+-----------------+----------------+---------------------+---------------------+------------+ | 1 | 松本 千代 | NULL | NULL | NULL | NULL | 2019-05-18 00:48:41 | 2019-05-18 01:05:50 | NULL | +----+-----------+------+-------+-----------------+----------------+---------------------+---------------------+------------+ 1 row in set (0.00 sec) |
それではまずORMによる実行を行っていきたいと思います。
押下後、フォームから消えました。
データベースの確認結果が以下です。
1 2 3 4 5 6 7 |
MariaDB [testdb]> select * from test_users; +----+-----------+------+-------+-----------------+----------------+---------------------+---------------------+---------------------+ | id | name | age | email | living_together | family_address | created_at | updated_at | deleted_at | +----+-----------+------+-------+-----------------+----------------+---------------------+---------------------+---------------------+ | 1 | 松本 千代 | NULL | NULL | NULL | NULL | 2019-05-18 00:48:41 | 2019-05-18 09:35:59 | 2019-05-18 09:35:59 | +----+-----------+------+-------+-----------------+----------------+---------------------+---------------------+---------------------+ 1 row in set (0.00 sec) |
復活の呪文を押下しますと、レコードが復活していることが確認できます。
1 2 3 4 5 6 7 |
MariaDB [testdb]> select * from test_users; +----+-----------+------+-------+-----------------+----------------+---------------------+---------------------+------------+ | id | name | age | email | living_together | family_address | created_at | updated_at | deleted_at | +----+-----------+------+-------+-----------------+----------------+---------------------+---------------------+------------+ | 1 | 松本 千代 | NULL | NULL | NULL | NULL | 2019-05-18 00:48:41 | 2019-05-18 01:05:50 | NULL | +----+-----------+------+-------+-----------------+----------------+---------------------+---------------------+------------+ 1 row in set (0.00 sec) |
ORMによる削除に関しては「deleted_at」に日付が挿入され、論理削除がちゃんと機能出来ました。
続けて行っていきたいと思います。次はDBファサードによるdeleteを実行していきたいと思います。
押下後、レコードが削除されます。
1 2 |
MariaDB [testdb]> select * from test_users; Empty set (0.00 sec) |
ここでレコードが物理削除されてしまいました。
この後、truncateとクエリビルダについても同様に物理削除となってしまうことが確認できました。
イ.論理削除済みのレコード
今度は一度論理削除したレコードに対してDBファサードの削除を実行します。
まずはORMを使用し、論理削除を行います。
1 2 3 4 5 6 7 |
MariaDB [testdb]> select * from test_users; +----+-----------+------+-------+-----------------+----------------+---------------------+---------------------+---------------------+ | id | name | age | email | living_together | family_address | created_at | updated_at | deleted_at | +----+-----------+------+-------+-----------------+----------------+---------------------+---------------------+---------------------+ | 1 | 杉山 千代 | NULL | NULL | NULL | NULL | 2019-05-18 11:20:37 | 2019-05-18 11:21:27 | 2019-05-18 11:21:27 | +----+-----------+------+-------+-----------------+----------------+---------------------+---------------------+---------------------+ 1 row in set (0.00 sec) |
その後DBファサードのdeleteを実行します。
1 2 |
MariaDB [testdb]> select * from test_users; Empty set (0.00 sec) |
こちらに関してもDBファサードのdeleteとtruncate、クエリビルダの削除を実行するとレコードごと削除を行います。
まとめ
ソースコードまで追ってはないのではっきりとしたことは分かっていませんが、ORM以外での処理はLaravel内のモデルを経由しないためか、
論理削除は実行されませんでした。
この影響は、論理削除以外にも処理速度など大きく表れるか、という疑問も残る結果になりました。
また、そのあたりについて実証してみたいと思います。