Capítulo 4 de 9

Tutorial Laravel 13 #4: panel de administración con Filament

verificado el 7 septiembre 2026 · 7 min

Respuesta rápida

Laravel no proporciona back-office. Esta serie usa ahora Filament: composer require filament/filament y después php artisan filament:install --panels. Voyager, empleado en las versiones anteriores, figura como abandonado en Packagist y ya no se instala en Laravel 13.

Al final de este capítulo, tendrás un back-office Filament instalado en /admin, con una cuenta de administrador y una regla de acceso explícita.

Laravel no trae back-office. Hay que elegir uno, y esa decisión compromete al proyecto durante años. Este capítulo explica por qué esta serie ha cambiado de paquete y después instala Filament.

Por qué ha cambiado este capítulo

Las versiones anteriores de este tutorial usaban Voyager. Ya no es sostenible.

tcg/voyager está marcado como abandonado en Packagist. Su última versión, la 1.8.0, data de septiembre de 2024 y declara una compatibilidad que se detiene en Laravel 11. En un proyecto Laravel 13, Composer se niega sin más a instalarlo: primero se bloquea por los avisos de seguridad que afectan a todas sus versiones, y su restricción a Laravel 11 haría fallar de todos modos la resolución.

Esa es la lección útil del episodio: un tutorial que se apoya en un paquete mantenido por un solo equipo se vuelve falso el día en que ese equipo lo deja. La alternativa, por tanto, debe valorar la salud del proyecto tanto como sus funcionalidades.

Lo que se ha examinado

Cuatro candidatos, medidos el 2 de septiembre de 2026 con los datos públicos de Packagist:

Paquete Última versión Laravel 13 Descargas / mes
tcg/voyager 1.8.0 (sept. 2024) no 23.614, pero abandonado
ilsondev/voyager 2.2.1 (jul. 2026) 1
orchid/platform 14.53.0 (marzo 2026) 27.227
filament/filament 5.7.8 (agosto 2026) 3.324.015

ilsondev/voyager es un fork que funciona: se instaló en un Laravel 13.30.1 recién creado para esta revisión, y el panel, la herramienta BREAD, la mediateca, el constructor de menús y los ajustes responden todos correctamente. Pero suma 12 descargas y una estrella, y todas sus versiones se publicaron en tres días de julio de 2026. Además arrastra intervention/image en su versión 2, cuya rama ya no es la rama actual. Recomendar este fork a quien empieza sería repetir exactamente el error que rompió este tutorial.

Dicho esto, si mantienes una aplicación Voyager existente y lo que buscas primero es pasar a Laravel 13 sin reescribirlo todo, este fork es una pasarela honesta. Trátalo como una etapa, no como un destino.

La elección: Filament

Filament gana por tres razones.

  • No va a desaparecer mañana. 3,3 millones de descargas al mes, cien veces la audiencia de todas las alternativas juntas, y versiones publicadas cada semana.
  • El back-office es código. Voyager guardaba la configuración de las pantallas en la base de datos, montada a golpe de clic en una interfaz. En Filament, cada pantalla es una clase PHP versionada con el resto del proyecto. Se relee, se compara y se despliega como cualquier otro archivo.
  • Es una competencia que sirve. Filament es hoy el back-office por defecto del ecosistema Laravel. Lo que aprendas aquí te lo vas a encontrar en un proyecto real.

Hay dos alternativas de pago que conviene conocer, aunque no hacen falta para este tutorial: Nova, la solución oficial del equipo de Laravel, y Backpack.

Instalar Filament

La extensión intl debe estar presente

Filament exige la extensión de PHP intl. Compruébala antes de instalar:

bash
php -m | grep intl

Si el comando no devuelve nada, la instalación fallará con este mensaje, que no menciona Filament y deja perplejo:

code
filament/support[...] require ext-intl * -> it is missing from your system.
Install or enable PHP's intl extension.

El primer capítulo ya aconsejaba comprobarla por este motivo. En Debian o Ubuntu, sudo apt install php8.4-intl. En macOS con Homebrew, la extensión ya está. En Windows con WampServer, desmarca y vuelve a marcar php_intl en el menú de extensiones de PHP. Después reinicia el servidor.

El paquete y el panel

bash
composer require filament/filament
php artisan filament:install --panels

El segundo comando crea app/Providers/Filament/AdminPanelProvider.php, publica los archivos CSS y JavaScript del panel en public y vacía las cachés.

code
INFO  Successfully published assets!
INFO  Successfully upgraded!

Crear una cuenta

bash
php artisan make:filament-user

El comando pide un nombre, una dirección de correo y una contraseña, y luego confirma:

code
INFO  Success! admin@gekkode.test may now log in at http://localhost:8000/admin/login.

Ve a http://127.0.0.1:8000/admin e inicia sesión. El panel está vacío: es normal, todavía no se le ha confiado nada. El capítulo 6 le añadirá los artículos, las categorías y las etiquetas.

Controlar quién entra

En entorno local, Filament deja entrar a cualquier usuario autenticado. En cualquier otro sitio, rechaza a todo el mundo con un error 403 mientras el modelo User no diga quién tiene derecho a entrar. Es una seguridad por defecto, pero sorprende: el panel funciona en tu máquina y devuelve 403 en cuanto lo pones en línea.

La regla se declara implementando la interfaz FilamentUser. Conserve todo lo que el modelo incluido ya contiene, los atributos #[Fillable] y #[Hidden] igual que el método casts(): solo se añaden la interfaz y el método canAccessPanel().

app/Models/User.php
<?php

namespace App\Models;

use Database\Factories\UserFactory;
use Filament\Models\Contracts\FilamentUser;
use Filament\Panel;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\Hidden;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

#[Fillable(['name', 'email', 'password'])]
#[Hidden(['password', 'remember_token'])]
class User extends Authenticatable implements FilamentUser
{
    /** @use HasFactory<UserFactory> */
    use HasFactory, Notifiable;

    public function canAccessPanel(Panel $panel): bool
    {
        return str_ends_with($this->email, '@gekkode.test');
    }

    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }
}

Sustituye el dominio por el tuyo. Esta regla es deliberadamente simplista: basta para un proyecto de aprendizaje, no para un sitio público. Un proyecto real se apoya en una columna is_admin o en un sistema de roles.

El comportamiento se verifica con dos pruebas. La segunda cuenta tanto como la primera: en el entorno de pruebas, Filament rechaza a todo el mundo mientras la regla no exista, así que una prueba que solo comprobara el rechazo pasaría incluso sin ella.

tests/Feature/AdminTest.php
<?php

namespace Tests\Feature;

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class AdminTest extends TestCase
{
    use RefreshDatabase;

    public function test_un_email_hors_domaine_est_refuse(): void
    {
        $user = User::factory()->create(['email' => 'intrus@example.com']);

        $this->actingAs($user)->get('/admin')->assertForbidden();
    }

    public function test_un_email_du_domaine_est_accepte(): void
    {
        $user = User::factory()->create(['email' => 'admin@gekkode.test']);

        $this->actingAs($user)->get('/admin')->assertOk();
    }
}

Migrar desde la versión Voyager de este tutorial

Si habías seguido este tutorial en su versión Voyager, esto es lo que cambia.

  • Las tablas de Voyager desaparecen. La instalación creaba diez: data_types, data_rows, menus, menu_items, settings, roles, permissions, permission_role, user_roles y translations. Ninguna sirve ya. Tus tablas de negocio,posts, categories, tags, post_tag, users, se conservan tal cual: Filament se engancha a ellas sin migración de datos.
  • La configuración BREAD se pierde, y es intencionado. Lo que habías montado a clics en Voyager se reescribe en PHP, una sola vez, en los archivos del capítulo 6. Es media hora de trabajo, tras la cual la configuración queda versionada.
  • La función setting() ya no existe. Voyager la proporcionaba para leer el título y la descripción del sitio. Sustituye setting('site.title') por config('app.name'), o por tus propios ajustes.
  • El apaño del slug posts deja de ser necesario. Voyager traía su propio modelo Post, lo que obligaba a renombrar el slug de la tabla a blog-posts y a corregir una plantilla del paquete. Filament no impone ningún modelo, así que el conflicto desaparece.
  • La dirección del back-office no cambia. Filament también se instala en /admin.

Para quitar Voyager de un proyecto existente: composer remove tcg/voyager, y después elimina sus tablas y la carpeta resources/views/vendor/voyager si existe. Haz una copia de seguridad de la base de datos antes.

Lo que este capítulo no ha tratado

Los roles y permisos finos, la personalización del tema del panel y los widgets del escritorio quedan fuera del alcance de esta serie. La documentación de Filament los cubre.

El capítulo siguiente construye la página de inicio del sitio público.

Errores frecuentes

ext-intl is missing from your system Filament exige la extensión intl. El mensaje de composer no nombra a Filament y deja perplejo. Instala php-intl y relanza la instalación.
El panel devuelve 403 fuera del entorno local Filament rechaza a todo el mundo mientras el modelo User no implemente FilamentUser::canAccessPanel(). El panel funciona en local y se rompe al publicar.
Composer se niega a instalar tcg/voyager El paquete se detiene en Laravel 11 y está marcado como abandonado. No existe una versión compatible con Laravel 13.
Newsletter

Las nuevas pruebas, tutoriales y proyectos, por correo.

Pruebas reproducibles, código versionado, resultados fechados. Nunca spam.