【Laravel】論理削除を利用して退会機能を実装する

##論理削除？物理削除？

<dl>
<dt>論理削除</dt>
 <dd>実際にデータを削除せずに、削除されたとみなすフラッグを立てることであたかもユーザには削除されたようにすること。（DB上からは消えてはいない。）
処理速度は物理削除よりも早い。</dd>
<dt>物理削除</dt>
<dd>実際にDBから削除されてデータは空っぽになる。復元はできない。</dd>

##実装編

Laravl 5.5.50 を使用
####マイグレーションファイルの作成
Userテーブルに論理削除用のカラムを作成するためにマイグレーションファイルを作成する。

```
$ php artisan make:migration add_column_softDeletes_users_table --table=users
```

次にマイグレーションファイルを記述していく。
このとき、論理削除を利用して退会機能を実装しようとすると、退会後同じメールアドレスで再登録しようとすると登録できないという問題が起きます。
これはemailカラムのUNIQUE制約のため再登録ができなくなっているためです。
そのため一度emailのユニークキーを削除してemailとdeleted_atを複合ユニーク制約とします。
複合ユニーク制約にすることでたとえメールアドレスが同じでもdeleted_atは重複することがないので再登録が可能になります。

参考　https://readouble.com/laravel/5.5/ja/migrations.html#indexes

```php:add_column_softDeletes_users_table.php
//中略
class AddColumnSoftDeletesUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->softDeletes()
            $table->dropUnique('users_email_unique');
            $table->unique(['email','deleted_at'],'users_email_unique');;
        });
    }

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

マイグレーションファイルの記述ができたらマイグレーションを実行する。

```
$ php artisan migrate
```

####モデル（User.php）への記述
```User.php
namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\SoftDeletes; //追記
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;
    
    use SoftDeletes; //追記

   //中略
   protected $dates = ['deleted_at']; //追記

  //以下略
```

use SoftDeleteと記述することでSoftDeleteトレイトを使用すると宣言する。
またメンバ変数＄datesにdeleted_atをセットすることでモデル側で論理削除をできるようにする。

##ユーザー登録時のバリデーション条件の変更
app  > Http  > Controllers  >  Auth  > RegisterController.php

```RegisterController.php
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|string|max:255',
            //'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 変更前
            'email' => 'required|string|email|max:255|unique:users,email,NULL,id,deleted_at,NULL',　//変更後
            'password' => 'required|string|min:6|confirmed',
        ]);
    }
```
これはuserテーブルでemailのカラムがidカラムがNULLのものを除き、deleted_atがNULLのものが存在するかを確認するという内容になる。
※uniqueパラメーターの内容
第１引数　　→　　テーブル（必須）
第２引数　　→　　カラム（必須）
第３引数　　→　　除外データ
第４引数　　→　　除外カラム名（デフォルトはidカラム）
第５条件　　→　　追加条件のカラム
第６条件　　→　　追加条件の内容

参考
[【Laravel】 バリデーション - unique ルール（論理削除-SoftDeletes 対応)](https://qiita.com/namari/items/a7d4cd085ba3e717358c)
[Laravelのバリデーションで指定できる内容をざっくりまとめ直しました。]
(https://qiita.com/fagai/items/9904409d3703ef6f79a2)
[unique:テーブル,カラム,除外ID,IDカラム]
(https://readouble.com/laravel/5.5/ja/validation.html?header=unique:_%25E3%2583%2586%25E3%2583%25BC%25E3%2583%2596%25E3%2583%25AB_,_%25E3%2582%25AB%25E3%2583%25A9%25E3%2583%25A0_,_%25E9%2599%25A4%25E5%25A4%2596ID_,_ID%25E3%2582%25AB%25E3%2583%25A9%25E3%2583%25A0_)

##ルーティング
```web.php
//中略
Route::resource('users','UsersController',['only'=>['show','destroy']]); //destroyを追記
Route::get('users','UsersController@delete_confirm')->name('users.delete_confirm');　//警告画面に飛ばしたいため追記
//以下略
```
ボタンを押すといきなり退会するのはあまりにも不親切なのでボタンを押す→確認画面に移る→退会するという流れにする。

##UsersControllerへの記述
```UsersController.php
    public function destroy($id)
    {
        $user = User::find($id);
        $user->delete();
        return redirect('/');
    }
    
    public function delete_confirm()
    {
        return view('users.delete_confirm');
    }
```
退会処理が実行されたらトップページに飛ぶように記述。
##退会確認画面の実装
resources  >  views  >  users  >  delete_confirm.php
退会ページに飛ぶためにユーザーページの下部に「退会はこちらから」とリンクを作りました。

```delete_confirm.php
<div class="container">
    <div class="card border-dark mb-3">
        <div class="card-header">
          <h3>退会の確認</h3>
        </div>
      <div class="card-body">
        <p class="card-text">退会をすると投稿も全て削除されます。</p>
        <p class="card-text">それでも退会をしますか？</p>
      </div>
    </div>
    
    <div class="btn-group">
        {!! Form::open(['route'=>['users.destroy',Auth::user()->id],'method'=>'delete']) !!}
            {!!Form::submit('退会する',['class'=>'btn btn-danger'])!!}
        {!!Form::close()!!}
        
        <div class="ml-3">
            <a href="/" class="btn btn-primary">キャンセルする</a>
        </div>
    </div>
</div>
```

##おわりに
論理削除を用いて退会機能を実装するのは簡単でしたが、同時に学ぶことが多くて挑戦してみてよかったと思いました。
初の長い記事の投稿でしたので多少見苦しい点はあるとは思いますが、参考になったと感じたらLGTMお願いします…笑

##その他参考
[Laravelで論理削除(SoftDelete)](https://webxreal.com/laravel-softdelete/)
[[Laravel] Uniqueバリデーションで、論理削除を考慮するなど、条件をカスタマイズしたい](https://www.yoheim.net/blog.php?q=20190101)