joelbutcher/laravel-archivable
Add archiving to Laravel Eloquent models like SoftDeletes. Includes migration macros for archived_at, model trait, and query builder scopes/macros to archive/unarchive records and filter archived vs active results in fluent chains.
Install the package via Composer and immediately start archiving models with minimal setup.
composer require joelbutcher/laravel-archivable
Then in your migration, use the provided archivedAt() macro to add an archived_at column—identical to SoftDeletes but semantically clearer for archival workflows.
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('user_id');
$table->string('status');
$table->timestamps();
$table->archivedAt(); // Adds `archived_at` timestamp column
});
Next, attach the Archivable trait to your Eloquent model and begin using intuitive methods like archive(), unArchive(), and isArchived().
use LaravelArchivable\Archivable;
class Order extends Model
{
use Archivable;
// ...
}
First use case: accidentally deleted user account recovery — an admin can archive (not delete) a user record, preserve all related data, and easily restore it later via unArchive().
Default behavior
Archivable models are excluded by default in queries (withoutArchived scope is applied), mirroring SoftDeletes but with explicit archiving semantics.
Explicit query control
Use chainable scopes for precise control:
// Include archived items (e.g., admin dashboard)
$orders = Order::withArchived()->get();
// Show only archived items (e.g., "Archive" tab)
$archivedOrders = Order::onlyArchived()->get();
// Explicitly exclude archived (redundant but readable)
$activeOrders = Order::withoutArchived()->get();
Bulk operations
Archive or restore at scale:
Order::where('status', 'cancelled')->archive();
Order::onlyArchived()->unArchive();
Route model binding integration
Override implicit binding to restore archived resources on demand:
Route::get('/orders/{order}', function (Order $order) {
return $order->status;
})->withArchived(); // Allows fetching archived orders via route binding
Event-driven workflows
Hook into Archived, UnArchived events (added in v1.3.1):
Order::saved(fn ($order) => $order->wasArchived() && ! $order->isArchived()
? event(new OrderRestored($order))
: null);
Audit & reporting
Create reports spanning archived & active data:
$totalRevenue = Order::withArchived()
->sum('total');
Migration rollback safety
Use dropArchivedAt() in down migrations—not dropColumn('archived_at')—to prevent accidental table drops (fixed in v1.11.0).
Global scope override
Avoid calling withoutArchived() manually—it’s applied automatically. Calling it explicitly results in redundant/no-op scopes.
Soft Deletes conflict
Do not use both SoftDeletes and Archivable traits on the same model—they conflict on deleted_at/archived_at columns and scopes.
CLI migrations & seeders
Archive-seeded test data for realistic local environments:
$order = Order::factory()->create();
$order->archive(); // Simulate archived historical orders
API response consideration
Don’t expose archived_at in public responses unless intentional—exclude it via $hidden or resource transformations if sensitive.
Database index optimization
Add an index for performance on archive-heavy queries:
$table->index('archived_at');
Custom archiving logic
Override trait methods in your model for domain-specific behavior (e.g., cascading archive of related models):
public function archive(): bool
{
$this->comments()->each->archive();
return parent::archive();
}
How can I help you explore Laravel packages today?