<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('body');
$table->timestamps();
$table->softDeletes(); // Adds deleted_at column
});
}
public function down(): void
{
Schema::dropIfExists('posts');
}
};
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
protected $fillable = ['title', 'body'];
// Cascade soft delete to comments
protected static function booted(): void
{
static::deleting(function ($post) {
$post->comments()->delete();
});
static::restoring(function ($post) {
$post->comments()->restore();
});
}
public function comments()
{
return $this->hasMany(Comment::class);
}
}
<?php
// Soft delete a post
$post = Post::find(1);
$post->delete(); // Sets deleted_at to now
// Check if soft deleted
if ($post->trashed()) {
echo "Post is deleted";
}
// Query without soft deleted
$posts = Post::all(); // Excludes soft deleted
// Include soft deleted
$allPosts = Post::withTrashed()->get();
// Only soft deleted
$deletedPosts = Post::onlyTrashed()->get();
// Restore soft deleted
$post->restore();
// Force delete (permanent)
$post->forceDelete();
// Restore all deleted posts
Post::onlyTrashed()->restore();
// Force delete all trashed posts
Post::onlyTrashed()->forceDelete();
// Query with relationships
$posts = Post::withTrashed()
->with(['comments' => function ($query) {
$query->withTrashed();
}])
->get();
Soft deletes mark records as deleted without removing them from the database, enabling recovery and audit trails. I add SoftDeletes trait to models and a deleted_at timestamp column. Calling delete() sets deleted_at instead of removing rows. Soft-deleted models are automatically excluded from queries. The withTrashed() method includes deleted records, while onlyTrashed() retrieves only deleted ones. The restore() method undeletes records. Force deletion with forceDelete() permanently removes rows. Soft deletes work with relationships—cascading soft deletes keep data consistent. This pattern is essential for regulatory compliance, user data recovery, and maintaining referential integrity.