Laravelでデータ復活できる「ゴミ箱」機能をつくる

こんにちは。フリーランス・コンサルタント&エンジニアの 九保すこひ です。

さてさて、数日前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_atnullに更新します。
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で取得した削除データをループさせながら一覧を作ることになります。

テストしてみる

では、テーブルpostsproductsを全てソフトデリートした状態にしてテストしてみましょう!

では、一番上のposts10番を元に戻してみます。

削除リストから消えました。

念のため、データベースも確認してみましょう。

10番のデータだけdeleted_atnullになっています。
成功です😊✨

開発のご依頼お待ちしております
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします! by 九保すこひ

おわりに

ということで、今回はLaravelで「ゴミ箱」機能をつくってみました。

ゴミ箱機能があれば、間違って削除しても簡単に復活させることができますし、なによりユーザー側もビクビク使わなくてよくなるので、きっと喜ばれることでしょう😊✨

また、今回は2つのテーブルだけに「ゴミ箱」機能をつくりましたが、union()する部分で3つでも4つでも追加できるのでフレキシブルに対応ができると思います。(ただし、データ数が増えると速度が遅くなるので、その場合は「ゴミ箱」専用テーブルをつくる方がいいです)

地味に便利な機能なので、ぜひ皆さんもやってみてくださいね。

ではでは〜!

このエントリーをはてなブックマークに追加       follow us in feedly