laravelで視覚的に理解する論理削除

##論理削除とは？
論理削除とは実際にはデータを削除せず、見かけ上削除しているように見える技術である。
データベースにはデータが残っているので、必要な時にそのレコードを復元したり、一部の権限の人にはレコードを公開したりできる。
例えば、管理者はそのレコードを確認できるがユーザーはその内容を確認できないように設定することができる。
パソコンの不要ファイルを削除してゴミ箱に格納するイメージに近い。

Laravlではマイグレーションファイルとモデルへの設定で論理削除用のカラムを設定できるので視覚的に検証してみることにした。

##マイグレーションファイルの準備

今回はpeopleテーブルをもとに論理削除する。
論理削除をする上で対応するpeopleテーブルに対応するモデルも必要となる。
Person.phpは以下のようにする。

```php:Person.php
<?php

namespace App\Models;

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

class Person extends Model
{
    use SoftDeletes;
    use HasFactory;
}

```
まずはrunメソッドを以下のように編集してマイグレーションする。

```php:xxx_create_people_table
public function up()
    {
        Schema::create('people', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->integer('age');
            $table->timestamp('created_at')->useCurrent()->nullable();
            $table->timestamp('updated_at')->useCurrent()->nullable();
            $table->softDeletes();
        });
    }
```

また、ダミーデータも挿入するとテーブルの中身は以下のようになる。
![スクリーンショット 2021-05-01 17.08.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/926931/1200215c-9d4e-3d03-c92c-c4925bd188dd.png)

##コントローラーの作成

今回はSoftDeleteControllerという名前で作成する。

コントロローラーは以下のように記述します。

```php:SoftDeleteController
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Person;
class SoftDeleteController extends Controller
{
    public function index()
    {
        $people = Person::all(); // softdeletesカラムがnullの物を取得
        $deleted = Person::onlyTrashed()->get(); // 論理削除済のデータ取得
        $param = ['people' => $people,'deleted' => $deleted];
        return view('delete', $param);
    }
    
 
    /*
     * 論理削除
     */
    public function delete($id)
    {
        Person::find($id)->delete(); // softDelete
 
        return redirect()->to('delete');
    }
    public function restore($id)
    {
        Person::onlyTrashed()->find($id)->restore(); // restoreメソッドで復元
 
        return redirect()->to('delelte');
    }
 
    /*
     * force delete Person
     */
    public function forceDelete($id)
    {
        Person::onlyTrashed()->find($id)->forceDelete(); //forceDeleteメソッドで物理削除
 
        return redirect()->to('delete');
    }
}

```

###論理削除されたレコードの取得
indexアクションの
`Person::onlyTrashed()->get();` のように
`モデル名::onlyTrashed()->get();` で論理削除されたレコードを取得できる。

###論理削除
deleteアクションの
`Person::find($id)->delete();`のように
`モデル名::find(対象のレコード)->delete();` で論理削除が実行できる。

###論理削除されたレコードの復元
restoreアクションの
`Person::onlyTrashed()->find($id)->restore();`のように
`モデル名::onlyTrashed()->find(対象のレコード)->restore();` で対象の論理削除されたレコードを復元ができる。

###論理削除したレコードの完全削除
forcedeleteアクションの
`Person::onlyTrashed()->find($id)->forceDelete();` で対象の論理削除されたレコードを完全削除（物理削除）できる



##ビューページ
ビューページとしてdeleteディレクトリを作成し、index.phpを作成する。

```php:index.php
<style>
   th {
      background-color: black;
      color: white;
      padding: 5px 30px;
    }
    td {
      border: 1px solid black;
      padding: 5px 30px;
      text-align: center;
    }
</style>
<body>
  <a href="/delete/create">メンバー追加</a>
  <h2>未削除</h2>
  <table>
        <tr>
            <th>ID</th>
            <th>名前</th>
            <th>削除ボタン</th>
        </tr>
        @forelse ($people as $person)
            <tr>
                <td>{{ $person->id }}</td>
                <td>{{ $person->name }}</td>
                <td>
                    <a href="/delete/delete/{{ $person->id}}">削除</a>
                </td>
            </tr>
        @empty
            <tr>
                <td>-</td>
                <td>-</td>
                <td>-</td>
            </tr>
        @endforelse
  </table>
  <h2>削除済</h2>
  <table>
          @forelse ($deleted as $delete)
              <tr>
                  <td>{{ $delete->id }}</td>
                  <td>{{ $delete->name }}</td>
                  <td>
                      <a href="/delete/restore/{{ $delete->id}}">復旧</a>
                      <a href="/delete/force-delete/{{ $delete->id}}">完全削除</a>
                  </td>
              </tr>
          @empty
              <tr>
                  <td>-</td>
                  <td>-</td>
                  <td>-</td>
              </tr>
          @endforelse
  </table>
</body>
```
ルーティングを設定してこのdelete/index.blade.phpにアクセスすると以下のようになる。

![スクリーンショット 2021-05-01 18.41.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/926931/c7f833b7-956f-a032-d123-578370876848.png)

ルーティングも設定する。

```php:web.php
Route::group(['prefix' => 'delete'], function () {
    Route::get('', [SoftDeleteController::class,'index'])->name('delete');// 一覧表示
    Route::get('delete/{id}', [SoftDeleteController::class,'delete']); // 論理削除
    Route::get('restore/{id}', [SoftDeleteController::class,'restore']); // 復元
    Route::get('force-delete/{id}', [SoftDeleteController::class,'forceDelete']); // 完全削除
});
```

実際にdeleteにアクセスして削除ボタンを押して、論理削除してみる。
画面は下記のようになる。
![スクリーンショット 2021-05-01 18.51.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/926931/49e2a09d-b44c-b79e-064f-efbb8ce25132.png)
また、テーブルには削除された時刻が表示される。
![スクリーンショット 2021-05-01 18.53.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/926931/27a532ba-dab9-125d-9388-7254b9a29891.png)

削除済のレコードの復旧ボタンを押して、復元するとそのレコードは未削除の欄に戻り、テーブルの削除された時間もNULLになる。
また、完全削除ボタンを押すとテーブルからレコードが削除されて、ブラウザからも存在がなくなる。