# 🧹 Laravel Model Pruning in Laravel 12 — Automatically Clean Up Old Records

By Dinesh Uprety
Published: 2025-11-01
Updated: 2025-11-02
Canonical: https://laranepal.com/blog/laravel-model-pruning-in-laravel-12-automatically-clean-up-old-records

# 🧹 Laravel Model Pruning in Laravel 12 — Automatically Clean Up Old Records

Keeping your database lean and healthy is essential for performance and maintainability.
Over time, tables fill up with old logs, expired sessions, or temporary uploads — data you don’t really need anymore.

Instead of writing custom cleanup scripts, **Laravel** gives you a simple, elegant solution: **Model Pruning**.
Let’s explore how the [**Prunable**](https://api.laravel.com/docs/11.x/Illuminate/Database/Eloquent/Prunable.html) and [**MassPrunable**](https://api.laravel.com/docs/11.x/Illuminate/Database/Eloquent/MassPrunable.html) traits work in **Laravel 12**, with real-world examples and best practices.

---

## 🚀 What Is Model Pruning?

Model pruning is a built-in Laravel feature that automatically deletes old or unnecessary records from your database.

You simply:

1. Add the `Prunable` or `MassPrunable` trait to your Eloquent model
2. Define which records should be deleted
3. Schedule the `model:prune` Artisan command to run periodically

Laravel takes care of the rest. 👌

---

## 🧩 Example 1: Auto-Delete Temporary Uploads After 7 Days

Imagine you have a table for temporary file uploads.
You want to delete both the file and its record after 7 days.

### `app/Models/TemporaryUpload.php`

```php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Prunable;
use Illuminate\Support\Facades\Storage;

final class TemporaryUpload extends Model
{
    use Prunable;

    public function prunable()
    {
        // Delete records older than 7 days
        return static::where('created_at', '<=', now()->subDays(7));
    }

    // Called before deleting each record
    protected function pruning(): void
    {
        if ($this->file_path) {
            Storage::delete($this->file_path);
        }
    }

    // Called after deletion (optional)
    protected function pruned(): void
    {
        \Log::info("Pruned upload #{$this->id}");
    }
}
```

### Schedule the prune command

```php
// routes/console.php
<?php

declare(strict_types=1);

use Illuminate\Support\Facades\Schedule;

Schedule::command('model:prune')->daily();
```

✅ **Done!**
Every day, Laravel will:

- 🕓 Find uploads older than **7 days**
- 🗑️ Delete their files
- 💾 Remove their database records

---

## ⚡ Example 2: Pruning Millions of Old Logs (Fast!)

If you’re cleaning up **huge** tables like `activity_logs`, you don’t want to delete records one by one.
That’s where the **MassPrunable** trait comes in — it deletes in bulk.

### `app/Models/ActivityLog.php`

```php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\MassPrunable;

final class ActivityLog extends Model
{
    use MassPrunable;

    public function prunable()
    {
        return static::where('created_at', '<=', now()->subMonths(6));
    }
}

```

💡 **Tip:**
`MassPrunable` skips model events and callbacks — it’s faster, but you can’t hook into `pruning()` or `deleted` events.

---

## 🧠 Useful Artisan Commands

Dry-run first to check what will be deleted:

```php
php artisan model:prune --pretend
```

Only prune specific models:

```php
php artisan model:prune --model="App\\Models\\TemporaryUpload"
```

Exclude models from pruning:

```php
php artisan model:prune --except="App\\Models\\User"
```

---

## 🧰 Common Use Cases

- 🗂 **Temporary uploads or previews** – delete files after a few days
- 📜 **Activity / audit logs** – keep only the last 6 months
- 🕰 **Soft-deleted models** – permanently delete after 90 days
- 💬 **Notifications or tokens** – clean expired entries
- 📦 **Large data archives** – control database size over time

---

## ⚙️ Best Practices

- 🔍 **Test first** using `--pretend` to confirm your query.
- 🧑‍💻 **Use `Prunable`** if you need model events or file deletion hooks.
- ⚡ **Use `MassPrunable`** for very large datasets (no per-model callbacks).
- 🧾 **Log or monitor** how many records get deleted each run.
- 🧩 **Combine with `SoftDeletes`** for time-based permanent removal.
- 🧠 **Document your retention policy** — know what data you’re deleting.
- 🕐 **Schedule during off-peak hours** for heavy deletes.

## ✅ Summary

Laravel’s **Model Pruning** gives you a clean, framework-native way to handle old data — no custom cron scripts, no manual deletes.

You just:

1. 🧩 **Define your pruning rule**
2. 🕓 **Schedule the command**
3. 🧹 **Let Laravel keep your database clean automatically**

It’s simple, powerful, and perfect for production-grade apps in **Laravel 12**.

---

💬 **Have you used the Prunable trait yet?**
Share your favorite use-case or pruning strategy in the comments below!

---
