1. Home
  2. PHP
  3. Laravel
  4. Laravelの認証機能でユーザ情報の論理削除(ソフトデリート)を実装する

Laravelの認証機能でユーザ情報の論理削除(ソフトデリート)を実装する

  • 公開日
  • 更新日
  • カテゴリ:Laravel
  • タグ:PHP,Laravel,migration,Auth,EloquentORM
Laravelの認証機能でユーザ情報の論理削除(ソフトデリート)を実装する

ユーザ管理機能については、デフォルトでは「ログイン」「ユーザ登録」「パスワードリセット」の機能がありますが、要件によってはもっと多くの機能と情報が必要になる場合も多々あります。

今回からユーザ情報に関する機能の拡張・追加を行いますが、中でも管理機能で良く使われる「論理削除(ソフトデリート)」「最終ログイン」「ロール(権限)機能」を追加していきます。

今回は Laravel認証機能の拡張第2弾として、ユーザ情報の論理削除(ソフトデリート)機能を実装します。

Contents

  1. 開発環境
  2. ユーザの論理削除(ソフトデリート)について
  3. 論理削除用のカラム追加
  4. モデル側の設定
  5. 論理削除のアクション
  6. 論理削除されたユーザの取得と復活処理

開発環境

今回の開発環境は以下の通りです。

  • Linux CentOS 7
  • Apache 2.4
  • MySQL 5.7
  • PHP 7.1/7.2
  • Laravel

Laravel のバージョンに関しては 5.6/5.5/5.4/5.3 にて動作確認済みです。

また、laravel のソースディレクトリを「laravel/」とします。

基本的な認証機能は導入済みである前提で進めますので、導入までに関しては
Laravel の認証機能でログイン/ユーザ登録/パスワードリセットなどの管理画面を一撃構築する(基本&入門編)
を確認してください。

ユーザの論理削除(ソフトデリート)について

ユーザを削除する際に、物理削除を行う場合は単純に delete メソッドを叩けば良いですが、論理削除を行いたい場合は削除フラグ的なものを立て管理していく必要があります。

以下は、マイグレーションを実行し作成したデフォルトでの Users テーブルのカラム情報です

mysql> show columns from users;
+----------------+------------------+------+-----+---------+----------------+
| Field          | Type             | Null | Key | Default | Extra          |
+----------------+------------------+------+-----+---------+----------------+
| id             | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| name           | varchar(255)     | NO   |     | NULL    |                |
| email          | varchar(255)     | NO   | UNI | NULL    |                |
| password       | varchar(255)     | NO   |     | NULL    |                |
| remember_token | varchar(100)     | YES  |     | NULL    |                |
| created_at     | timestamp        | YES  |     | NULL    |                |
| updated_at     | timestamp        | YES  |     | NULL    |                |
+----------------+------------------+------+-----+---------+----------------+

デフォルトでは、保持するユーザ情報も最小限の状態なので、論理削除を管理するものは何もありません。

ただし、Laravel では論理削除のための機能も備わっており、それをこれから実装していきます。

論理削除用のカラム追加

ユーザの論理削除を行う為に、users テーブルにカラムを追加します。

laravel ルートディレクトリに移動し、以下の artisan コマンドを叩いてマイグレーションファイルを追加します。

# Laravel ルートディレクトリへ移動する
cd /path/to/laravel

# artisan コマンドでマイグレーションファイルを追加する
php artisan make:migration add_column_softDeletes_users_table --table=users

# 実行結果
[demo@localhost laravel]# php artisan make:migration add_column_softDeletes_users_table --table=users
Created Migration: 2019_04_14_123848_add_column_softDeletes_users_table

laravel/database/migrations 配下に、生成されたマイグレーションファイルが配置されます。

laravel/database/migrations/2019_04_14_123848_add_column_softDeletes_users_table.php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddColumnSoftDeletesUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            //
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            //
        });
    }
}

マイグレーションファイルに、以下のように記述を行います。

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddColumnSoftDeletesUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
          $table->softDeletes();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
          $table->dropColumn('deleted_at');
        });
    }
}

$table->softDeletes(); にてカラムを追加する事で、論理削除を行う事が出来ます。

マイグレーションを実行し、users テーブルにカラムを追加します。

# Laravel ルートディレクトリへ移動する
cd /path/to/laravel

# artisan コマンドでマイグレーションを実行する
php artisan migrate

# 実行結果
[demo@localhost laravel]# php artisan migrate
Migrating: 2018_01_13_084659_add_column_softDeletes_users_table
Migrated:  2018_01_13_084659_add_column_softDeletes_users_table

users テーブルを確認します。

mysql> show columns from users;
+----------------+------------------+------+-----+---------+----------------+
| Field          | Type             | Null | Key | Default | Extra          |
+----------------+------------------+------+-----+---------+----------------+
| id             | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| name           | varchar(255)     | NO   |     | NULL    |                |
| email          | varchar(255)     | NO   | UNI | NULL    |                |
| password       | varchar(255)     | NO   |     | NULL    |                |
| remember_token | varchar(100)     | YES  |     | NULL    |                |
| deleted_at     | timestamp        | YES  |     | NULL    |                |
| created_at     | timestamp        | YES  |     | NULL    |                |
| updated_at     | timestamp        | YES  |     | NULL    |                |
+----------------+------------------+------+-----+---------+----------------+

deleted_at カラムが追加されている事が確認できました。

この deleted_at カラムは、初期値として NULL が入りますが、ここに timestamp を挿入する事で、論理削除を行う事が出来ます。

mysql> select id, name, deleted_at from users;
+----+--------+------------+
| id | name   | deleted_at |
+----+--------+------------+
|  1 | user01 | NULL       |
|  2 | user02 | NULL       |
|  3 | user03 | NULL       |
|  4 | user04 | NULL       |
|  5 | user05 | NULL       |
|  6 | user06 | NULL       |
|  7 | user07 | NULL       |
|  8 | user08 | NULL       |
|  9 | user09 | NULL       |
| 10 | user10 | NULL       |
+----+--------+------------+

モデル側の設定

deleted_at カラムを用いたソフトデリート(論理削除)は Eloquent ORM にて行われますので、そのための設定をモデル側に記述します。

まずは Users テーブルのためのモデルを生成します。
laravel/app 配下に Models ディレクトリを作成します。そして、Laravel ルートディレクトリに移動し、以下の artisan コマンドを叩きます。

# Laravel ルートディレクトリへ移動する
cd /path/to/laravel

# artisan コマンドでモデルクラスを生成する
php artisan make:model Models/Users

# 実行結果
[demo@localhost laravel]# php artisan make:model Models/Users
Model created successfully.

laravel/app/Models 配下に Users.php が生成されます。

laravel
├─ app
│   ├─ Models
│   │   └─ Users.php
laravel/app/Models/Users.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Users extends Model
{
    //
}

ここに、ソフトデリート用の記述を行うと以下のようになります。

<?php

namespace App\Models;

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

class Users extends Model
{
    use SoftDeletes;

    protected $table = 'users';
    protected $dates = ['deleted_at'];
}

SoftDeletes トレイトを use し、メンバ変数 $dates に deleted_at をセットする事で、モデル側で論理削除を実行できるようになります。

論理削除のアクション

データベース側の準備ができたので、コントローラ側から論理削除のメソッドを実装します。

ユーザを論理削除する場合は、例えば以下のようなアクションを記述します。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Users;


class AccountController extends Controller
{
  public function deleteData(Request $request)
  {
    $user = Users::find($request->input('id'));
    $user->delete();
  }
}

ここでのポイントは、DB ファサードではなく、Eloquent ORM にて delete() メソッドを実行するという事です。

find() メソッドに削除対象の ID を渡してインスタンスを取得し、delete() メソッドを実行する事で deleted_at カラムにタイムスタンプが挿入され、論理削除が行われます。

# 論理削除後のユーザテーブル
mysql> select id, name, deleted_at from users;
+----+--------+---------------------+
| id | name   | deleted_at          |
+----+--------+---------------------+
|  1 | user01 | NULL                |
|  2 | user02 | NULL                |
|  3 | user03 | NULL                |
|  4 | user04 | NULL                |
|  5 | user05 | NULL                |
|  6 | user06 | NULL                |
|  7 | user07 | NULL                |
|  8 | user08 | NULL                |
|  9 | user09 | NULL                |
| 10 | user10 | 2018-01-13 18:11:13 | ← 論理削除が行われた
+----+--------+---------------------+

ちなみに、DB ファサードで delete() メソッドを実行すると、通常の物理削除になります。

実際にユーザ一覧を作成してみてみるとわかりますが、論理削除されたユーザは表示されません。

ただし、ユーザ一覧を取得する際も、DB ファサードではなく Eloquent ORM で取得を行う必要があります。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Users;

class AccountController extends Controller
{
  /**
   * 認証ユーザとユーザ一覧情報を返却する
   */
  public function index()
  {
    // Eloquent ORM でユーザ一覧を取得する
    $users = Users::all();

    return view('account.list', ['users' => $users]);
  }
}

どうしても DB ファサードでユーザ一覧を取得したい場合は、単純に Where 句を付ければ論理削除の形になります。

/**
 * ユーザ一覧を返却する
 * @return mixed
 */
public function getUsers()
{
  $users = DB::table($this->table)
    ->whereNull('deleted_at')
    ->get();
  return $users;
}

そういう意味では、DB ファサードでも deleted_at カラムにタイムスタンプを挿入すれば論理削除にはなります。

ただ、それやるのであれば普通にフラグメント管理してしまった方が良いかなと思うのでここでは割愛します。

論理削除されたユーザの取得と復活処理

通常、論理削除されたデータを参照する場面というのはあまりないと思うのですが、その辺のクエリも Laravel Eloquent ORM には一通り用意されているので紹介しておきます。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Users;

class AccountController extends Controller
{
  public function index()
  {
    // 【1】論理削除済みユーザも含めた一覧を取得する
    $users = Users::withTrashed()->get();

    // 【2】論理削除済みユーザのみを取得する
    $users = Users::onlyTrashed()->get();

    // 【3】論理削除されたユーザを復活させる
    Users::onlyTrashed()->where('id', 10)->restore();

    // 【4】ID 10 より上の論理削除済みユーザを復活させる
    Users::withTrashed()->where('id', '>', 10)->restore();

    // 【5】論理削除ではなく、物理削除を行う
    Users::withTrashed()->where('id', 10)->forceDelete();

    // 【6】論理削除ではなく、物理削除を行う(複数件)
    Users::withTrashed()->where('id', '>', 5)->forceDelete();
  }
}

論理削除済みデータも取得対象にする

// 【1】論理削除済みユーザも含めた一覧を取得する
$users = Users::withTrashed()->get();

withTrashed() メソッドを用いる事で、論理削除済みユーザも含めたユーザの一覧を取得できます。 where 句などの絞り込みも可能です。 論理削除済みデータのみを取得する

// 【2】論理削除済みユーザのみを取得する
$users = Users::onlyTrashed()->get();

onlyTrashed() メソッドを用いる事で、論理削除済みユーザのみを取得することが出来ます。 where 句などの絞り込みも可能です。 論理削除からの復活(解除)

// 【3】論理削除されたユーザを復活させる
Users::onlyTrashed()->where('id', 10)->restore();

論理削除済みユーザのモデルインスタンスに対して restore() メソッドを用いることで、論理削除からもとに戻すことが出来ます。

上記の例の場合、論理削除済みであるユーザ ID 「10 」のユーザに対して、リストアメソッドを当てています。

ちなみに上記例の場合、onlyTrashed() と Where 句にて、論理削除されたユーザ1件のみのモデルインスタンスを取得して restore() メソッドを発動させましたが、withTrashed() メソッドで論理削除されていないユーザも含めたモデルインスタンスに restore() メソッドを発動させても、リストアは論理削除したユーザにのみ適用され、生きているユーザデータについては無傷です。

そしてその場合、論理削除のユーザが複数件いたとしても、その全てが論理削除を解除されます。

// 【4】 ID 10 より上の論理削除済みユーザを復活させる
Users::withTrashed()->where('id', '>', 10)->restore();

物理削除を行う 基本的には論理削除で運用しますが、データ整理などで物理削除も必要になる場合もあります。 forceDelete() メソッドを用いる事で、論理削除ではなく、物理削除を行う事ができます。

// 【5】論理削除ではなく、物理削除を行う
Users::withTrashed()->where('id', 10)->forceDelete();

上記例の場合は、ユーザ ID 「10 」のユーザに対して、物理削除を行っています。 また、複数件同時に物理削除を行う事も可能です。その場合は対象を複数件にするだけです。

// 【6】論理削除ではなく、物理削除を行う(複数件)
Users::withTrashed()->where('id', '>', 5)->forceDelete();

上記例の場合は、ユーザ ID 「5 」より上のユーザ全てに対して、物理削除を行っています。 ## まとめ

以上で作業は完了です。
Eloquent ORM を使う事で、デフォルトの認証機能から少しの追加で論理削除を導入することができます。

Laravel の機能を使ってこうして論理削除を実装するもよし、削除フラグを作って自力で論理削除を実装するもよし。どう実装するかは個人の好みによると思いますので、要件によって実装しやすい形で進めていくのが良いかと思います。

Author

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級