Laravelでの論理削除(SoftDelete)はテーブルとモデルの設定だけで簡単に実現できます。
具体的にはテーブルにdeleted_atカラムを追加してモデルにSoftDeletesトレイトを利用します。
これで削除時にEloquentORMがdeleted_atカラムに日時データを登録することでモデル側で自動的に無視するようになります。
Userモデルで設定してみましょう!
目次
開発環境
Laravel 6.6.0
Userテーブル変更
マイグレーションファイルで「$table->softDeletes();」を追加することでdeleted_atカラムを作成することができます。
Userモデルで実装するには削除したユーザのメールアドレスはUNIQUE制約で再登録できないという問題があります。
emailとdeleted_atの2つのカラムでUNIQUE制約を設定します。
マイグレーションファイル作成
# php artisan make:migration add_column_softdeletes_users_table --table=users
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddColumnSoftdeletesUsersTable extends Migration
{
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->softDeletes();
$table->dropUnique('users_email_unique');
$table->unique(['email', 'deleted_at'], 'users_email_unique');
});
}
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('deleted_at');
$table->dropUnique('users_email_unique');
$table->unique('email', 'users_email_unique');
});
}
}
users_email_uniqueというキーでemailのUNIQUE制約がかかっているようなので削除してから再設定しています。
マイグレーション
# php artisan migrate
Userモデル変更
EloquentのSoftDeletesトレイトを使用します。
use Illuminate\Database\Eloquent\SoftDeletes; //追記
class Users extends Model
{
use SoftDeletes; // 追記
}
5.7までは
protected $dates = [‘deleted_at’];
の記述が必要だったが5.8からは「SoftDeletesトレイトは自動的にdeleted_at属性をDateTime/Carbonインスタンスへ変換します。」ので不要。
ユーザ登録時のEmailのバリデーション変更
ユーザ登録時のバリデーションにemailとdelete_atの複合UNIQUE制約を設定します。
deleted_atがNULLでないemailをユニークとします。
use Illuminate\Validation\Rule; // 追加
・・・
protected function validator(array $data)
{
return Validator::make($data, [
// 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], // 以下のように変更
'email' => [
'required', 'string', 'email', 'max:255',
Rule::unique('users', 'email')->whereNull('deleted_at'),
],
・・・
確認
以上で論理削除(SoftDelete)が実装できました。
$user = User::find($id);
$user->delete();
でdeleted_atに日時が登録され取得時には無視されることを確認しましょう。
注意事項
DBファサードでは機能しません
EloquentのSoftDeletesトレイトを使用していますのでDBファサードでは機能しません。
DBファサードを使用するときはdeleted_atを考慮したコードを書く必要があるというだけで実現できないということではありませんが・・・
結合先では機能しません
対象テーブルにUserを結合(JOIN)して取得するときは対象テーブルでは機能しますがusersテーブルでは機能しないので whereNull(users.deleted_at) を付加してやらないとダメです。
補足
削除したデータも取得
$user->withTrashed()->where(・・・);
削除したデータのみ取得
$user->onlyTrashed()->where(・・・);
完全削除(物理削除)
$user->forceDelete();
削除したデータを復元
$user->restore();