今回は
Laravel x Vue3 x Inertia環境にて
論理削除を実装する方法をまとめました。
そもそも論理削除とは
別名SoftDelete(ソフトデリート)
逆に完全にDBから消し去るのがハードデリート
論理削除のメリット、デメリット
メリット
- データの復元が簡単
誤って消去しても、Laravelの標準機能で一瞬で元の状態に戻せる - データが残る安心感
データが物理的に消えないため、削除履歴の追跡や分析にそのまま活用できる - Laravelが自動で除外
設定するだけで、通常の検索クエリから削除済みのデータを自動的に隠してくれる
データ紛失のリスクを抑えつつ、開発の手間もかからない点が最大の強み
デメリット
- データベースの容量が肥大化する
データが物理的に消去されず蓄積され続けるため、ストレージ容量を圧迫
長期的には、不要なデータを完全に消去する定期的なクリーニング(物理削除)の仕組みが必要。 - クエリのパフォーマンスが低下する
データ量が増えることに加え、Laravelが自動的に WHERE deleted_at IS NULL
という条件をすべての検索クエリに付与します。適切なインデックス(複合インデックスなど)
を設定しないと、検索速度が低下する可能性あり。 - 一意制約(UNIQUE)との相性が悪い
例えば「メールアドレス」にUNIQUE制約をかけている場合、論理削除されたユーザーが
使うアドレスと同じアドレスで新しいユーザーが登録できなくなります
(削除されたデータがまだデータベース上に存在するため)
論理削除の実装
手順はこんな感じ。
1:論理削除用のカラムを用意する
マイグレーションファイルの用意と記述、マイグレーション
2:ルーティング記述
3:コントローラーに記述
「本当に削除しますか?」といったメッセージも
表示できるようにする
4:モデルにも記述
5:ビューも記述
6:動作チェック
1:論理削除用のカラムを用意する
- マイグレーションファイルの用意と記述、マイグレーション
// create_deelete_tableと言う名前のマイグレーションファイル作成
php artisan make:migration create_Delete_table
その後マイグレーションファイルを編集
database\migrationsの中にあります。
この記述をpublic function up(): voidの中に入れる
Schema::table('products', function (Blueprint $table) {
$table->softDeletes();
});
下記のようにします。
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
//public function up(): voidの中を編集
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('products', function (Blueprint $table) {
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('_delete');
}
};
その後マイグレートして適用させる
sail artisan migrate
2:ルートに記述
通常のdelete処理と同じように書いていきます。
routes\web.php
Route::delete('/products/{product}', [ProductController::class, 'destroy'])->name('users.destroy');
3:コントローラーに記述
ついでに「本当に削除しますか?」も記述
Http\Controllers\ProductController.php
// deleteのところだけ抜粋
public function destroy(Product $product)
{
$product->delete(); // ソフトデリート実行(deleted_atに現在時刻が入る)
return redirect()->route('products.index')
->with('success', '商品を削除しました');
4:モデルに記述
モデルは二行ほど追加
namespaceの下に
use Illuminate\Database\Eloquent\SoftDeletes;
class Product extends Modelの下に
use SoftDeletes;
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Product extends Model
{
use SoftDeletes;
protected $fillable = [
'name',
//(省略)
];
}
5:ビューに記述
resources\js\pages\Products\Index.vue
scriptタグ内に削除処理とconfirmを追加
routerも使えるように import内に記述
<script setup lang="ts">
import { Head, Link , router } from '@inertiajs/vue3';
interface Props {
users: Array<{
name: string;
}>;
}
// propsに型を適用する
const props = defineProps<Props>();
// 削除処理とconfirmによる確認
const deleteProduct = (productId: number) => {
if (confirm('本当に削除しますか?')) {
router.delete(`/products/${productId}`);
}
};
</script>
template内のボタンにも処理を追加
<button
@click="deleteProduct(product.id)"
type="button"
class="text-red-600 hover:bg-red-100 px-3 py-1 border
border-red-500rounded">
削除
</button>
6:動作チェック
削除ボタンクリックで

scriptで設定したconfirm処理が来て OKを押すと

deleted_atに日付が入ることによって非表示にするようなイメージです。
しっかり動作してますね。
試しに NULLに戻したらどうなるか見てみます。

しっかり戻りました。





