
Archivar modelos en Laravel con LaravelArchivable\Archivable
Cuando tu app crece, borrar definitivamente registros ya no es opción. Soft Deletes ayuda, pero a veces necesitas “archivar” elementos sin eliminarlos ni mostrarlos por defecto (p. ej., clientes inactivos, pedidos antiguos o tickets resueltos).
Para eso existe Laravel Archivable, un trait que añade un flujo de archivado/desarchivado muy parecido a SoftDeletes, con macros de migración y scopes listos para usar. GitHub
¿Qué resuelve exactamente?
Oculta registros archivados de las consultas por defecto (gracias a un global scope).
Ofrece métodos expresivos:
,archive()
,unArchive()
y scopes comoisArchived()
,withArchived()
,withoutArchived()
.onlyArchived()
Incluye macros de migración para agregar y quitar la columna
sin boilerplate.archived_at
Permite route model binding que incluya archivados cuando lo necesites.
Instalación
composer require joelbutcher/laravel-archivable
La instalación es directa desde Composer. GitHub
Migración: agrega archived_at
sin dolor
archived_at
El paquete añade un macro a
Blueprint
para sumar la columna:use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; Schema::table('posts', function (Blueprint $table) { $table->archivedAt(); // crea 'archived_at' nullable con índice adecuado });
Y para revertir:
Schema::table('posts', function (Blueprint $table) { $table->dropArchivedAt(); });
Estas macros (
archivedAt
/ dropArchivedAt
) vienen incluidas en el paquete. Sugerencia: en bases de datos grandes, crea un índice sobre
si no lo añade tu driver para acelerararchived_at
/withoutArchived()
.onlyArchived()
Modelo Eloquent: activar el trait
namespace App\Models; use Illuminate\Database\Eloquent\Model; use LaravelArchivable\Archivable; class Post extends Model { use Archivable; // ... }
Al usar el trait, el scope global aplica
withoutArchived()
por defecto; es decir, tus queries ignoran lo archivado a menos que pidas lo contrario. API de uso diario
Archivar / desarchivar
$post = Post::first(); $post->archive(); // establece archived_at = now() $post->unArchive(); // limpia archived_at $post->isArchived(); // bool
Scopes de consulta
// Solo activos (no archivados) — por defecto $activos = Post::query()->get(); // equivalente a ->withoutArchived() // Incluir archivados $todo = Post::query()->withArchived()->get(); // Solo archivados $soloArchivados = Post::query()->onlyArchived()->get();
Route Model Binding con archivados
Por defecto, el binding implícito no recupera archivados. Si quieres permitirlo en una ruta concreta:
use App\Models\Post; use Illuminate\Support\Facades\Route; Route::get('/posts/{post}', function (Post $post) { return $post; })->withArchived();
Cuándo usar Archivable vs. SoftDeletes
Usa Archivable cuando:
Quieres una “baja” funcional (ocultar por defecto) sin que signifique “eliminado”.
Necesitas restaurar/consultar objetos archivados con frecuencia.
Deseas mantener separado el concepto de “archivado” del de “eliminado” (suave o duro).
Usa SoftDeletes cuando:
“Eliminar” (reversible) es el estado final esperado.
Tienes lógica de negocio o políticas que ya dependen de
.deleted_at
Ejemplo real: archivar clientes inactivos (comando)
// app/Console/Commands/ArchiveInactiveCustomers.php namespace App\Console\Commands; use App\Models\Customer; use Illuminate\Console\Command; class ArchiveInactiveCustomers extends Command { protected $signature = 'customers:archive-inactive {--days=180}'; protected $description = 'Archiva clientes sin actividad por N días'; public function handle(): int { $days = (int) $this->option('days'); $count = Customer::query() ->withoutArchived() ->where('last_activity_at', '<', now()->subDays($days)) ->get() ->tap(fn ($c) => $c->each->archive()) ->count(); $this->info("Archivados: {$count}"); return self::SUCCESS; } }
Programa el comando con
schedule:run
para mantener tu base “limpia”.Buenas prácticas
UI clara: marca elementos archivados (badge “Archivado”), añade filtros “Activos / Archivados / Todos”. Si usas Filament, existe un plugin que integra archivado desde la tabla. Filament
Políticas & permisos: protege
/archive
con Policies y registra eventos para auditoría si tu negocio lo requiere (por ejemplo, con owen-it/laravel-auditing). Laravel AuditingPackagistunArchive
Jobs/colas: si vas a archivar en lote, considera despachar un Job para no bloquear peticiones.
Reportes: usa
para métricas (p. ej., cuántos casos cerrados por mes).onlyArchived()
Índices: asegúrate de tener índices en
y campos de filtrado frecuentes para mantener el rendimiento.archived_at
Checklist de integración rápida
Instalar el paquete.
Agregar
en la migración de las tablas relevantes.archivedAt()
Añadir
en los modelos.use Archivable;
Ajustar listados y filtros con
.withArchived()/onlyArchived()
Proteger acciones con Policies y, si aplica, auditar cambios. puedes ver mi publicacion Laravel Auditing: Auditoría de Modelos para Aplicaciones Profesionales
(Opcional) Habilitar
en rutas que deban resolver modelos archivados->withArchived()