posts/inE3y6Cp5LS297O4wJU8dab77QYhkirCSIavap1X.png

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()
    ,
    isArchived()
    y scopes como
    withArchived()
    ,
    withoutArchived()
    ,
    onlyArchived()
    .

  • Incluye macros de migración para agregar y quitar la columna

    archived_at
    sin boilerplate.

  • 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

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

archived_at
si no lo añade tu driver para acelerar
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
    /
    unArchive
    con Policies y registra eventos para auditoría si tu negocio lo requiere (por ejemplo, con owen-it/laravel-auditing). Laravel AuditingPackagist

  • Jobs/colas: si vas a archivar en lote, considera despachar un Job para no bloquear peticiones.

  • Reportes: usa

    onlyArchived()
    para métricas (p. ej., cuántos casos cerrados por mes).

  • Índices: asegúrate de tener índices en

    archived_at
    y campos de filtrado frecuentes para mantener el rendimiento.

Checklist de integración rápida

  1. Instalar el paquete.

  2. Agregar

    archivedAt()
    en la migración de las tablas relevantes.

  3. Añadir

    use Archivable;
    en los modelos.

  4. Ajustar listados y filtros con

    withArchived()/onlyArchived()
    .

  5. Proteger acciones con Policies y, si aplica, auditar cambios. puedes ver mi publicacion Laravel Auditing: Auditoría de Modelos para Aplicaciones Profesionales

  6. (Opcional) Habilitar

    ->withArchived()
    en rutas que deban resolver modelos archivados

Share:

0 comentarios

Dejar un comentario