九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、数日前Togetter
でこのページが話題になっているのを発見しました。
ぼく「嫌な予感がするから警告いっぱい出したれ」データ削除は三重確認設計に→??「なんかデータ消えたんですけど?」
開発者にとっては「あるある」ネタだと思うのですが、この中で衝撃だったのは、「削除したらほんとに消えたんですけど?」の部分です😂
(うーん、逆に削除できなくても苦情になりますし・・・💦)
そして、この投稿者さんもおっしゃっているとおり論理削除(ソフトデリート)というのはとても重要なんだな、と再認識しました。
※ソフトデリートとは、実際にデータは削除せず「削除したものとする」というテクニックです。Laravel
ではdeleted_at
で判別をします。
そこで!
今回は、間違ってデータ削除しても復活できるように、「ゴミ箱」ページをLaravel
で作ってみることにしました。
ぜひ皆さんのお役に立てると嬉しいです😊✨
開発環境: Laravel 7.x
やりたいこと
テストとして以下2つのテーブルを用意し、ソフトデリートされたデータを復活させる「ゴミ箱」ページをつくります。
- posts
- products
※今回は2つですが、応用すればいくつでも復活ができるようになります。
では、実際にやってみましょう!
ソフトデリート設定する
ソフトデリートを設定する方法は、Laravel・データベースのデータ操作をご覧ください。
また、データベースは次のような形で用意してください。
データ復活する「ゴミ箱」ページをつくる
ルートをつくる
/routes/web.php
Route::get('/trash_box', 'TrashBoxController@index'); Route::get('/trash_box/restore/{table}/{id}', 'TrashBoxController@restore')->name('trash_box.restore');
内容としては上の行が「ゴミ箱」ページで、ここで削除済データをリスト表示します。そして、下の行が実際にデータを復活させるルートです。
コントローラーをつくる
次のコマンドで専用のコントローラーをつくりましょう。
php artisan make:controller TrashBoxController
そして中身を変更してください。
/app/Http/Controllers/TrashBoxController.php
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class TrashBoxController extends Controller { public function index() { // 一覧ページ ・・・ ① $query_1 = \DB::table('posts') ->select( 'id', \DB::raw("title as label"), \DB::raw("'posts' as table_name"), 'deleted_at' ) ->whereNotNull('deleted_at'); $query_2 = \DB::table('products') ->select( 'id', \DB::raw("name as label"), \DB::raw("'products' as table_name"), 'deleted_at' ) ->whereNotNull('deleted_at'); $trash_boxes = $query_2->union($query_1) ->orderBy('deleted_at', 'desc') ->orderBy('id', 'desc') ->paginate(); return view('trash_box')->with('trash_boxes', $trash_boxes); } public function restore($table, $id) { // データ復活 ・・・ ② \DB::table($table)->where('id', $id)->update([ 'deleted_at' => null ]); return back(); } }
ここは少し複雑なのでひとつずつ紹介していきます。
① 一覧ページ
削除されたデータを取得するのですが、取得するテーブルは「posts」と「products」の2つです。
そのため、union()
を使って一気にデータ取得するわけですが、その際に気をつけないといけないのが「フィールド名を合わせる」ということです。
つまり、2つのテーブルのフィールド名は一致していないため、as
を使って同じフィールド名を指定しているわけです。
最終的に取得するフィールドは、
- id ・・・ ここは共通
- label ・・・ 「products.name」か「posts.title」の内容
- table_name ・・・ テーブル名「posts」か「products」
- deleted_at ・・・ ここも共通
の4つになります。
② データ復活
テーブル名とIDでデータを特定し、deleted_at
をnull
に更新します。
(Laravel
では、deleted_at
に日付が入っていれば削除されたものとみなされます)
ビューをつくる
最後にindex()
で使うビューをつくります。
/resources/views/trash_box.blade.php
<html> <head> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"> </head> <body> <div class="p-5"> <table class="table"> <thead class="bg-light"> <tr> <th>ID</th> <th>テーブル</th> <th>ラベル</th> <th></th> </tr> </thead> @foreach($trash_boxes as $trash_box) <tbody> <tr> <td>{{ $trash_box->id }}</td> <td><span class="badge badge-light">{{ $trash_box->table_name }}</span></td> <td>{{ $trash_box->label }}</td> <td class="text-right"> <a href="{{ route('trash_box.restore', [$trash_box->table_name, $trash_box->id]) }}" class="btn btn-secondary" type="button">元に戻す</a> </td> </tr> </tbody> @endforeach </table> {{ $trash_boxes->links() }} </div> </body> </html>
この中では、TrashBoxController
で取得した削除データをループさせながら一覧を作ることになります。
テストしてみる
では、テーブルposts
とproducts
を全てソフトデリートした状態にしてテストしてみましょう!
では、一番上のposts
の10
番を元に戻してみます。
削除リストから消えました。
念のため、データベースも確認してみましょう。
10番のデータだけdeleted_at
がnull
になっています。
成功です😊✨
おわりに
ということで、今回はLaravel
で「ゴミ箱」機能をつくってみました。
ゴミ箱機能があれば、間違って削除しても簡単に復活させることができますし、なによりユーザー側もビクビク使わなくてよくなるので、きっと喜ばれることでしょう😊✨
また、今回は2つのテーブルだけに「ゴミ箱」機能をつくりましたが、union()
する部分で3つでも4つでも追加できるのでフレキシブルに対応ができると思います。(ただし、データ数が増えると速度が遅くなるので、その場合は「ゴミ箱」専用テーブルをつくる方がいいです)
地味に便利な機能なので、ぜひ皆さんもやってみてくださいね。
ではでは〜!