Capítulo 4 de 9

Tutorial Laravel 13 #4: painel de administração com Filament

verificado a 7 Setembro 2026 · 7 min

Resposta rápida

O Laravel não fornece back-office. Esta série passa a usar o Filament: composer require filament/filament e depois php artisan filament:install --panels. O Voyager, usado nas versões anteriores, está marcado como abandonado no Packagist e já não se instala em Laravel 13.

No fim deste capítulo, vais ter um back-office Filament instalado em /admin, com uma conta de administrador e uma regra de acesso explícita.

O Laravel não fornece back-office. Tens de escolher um, e essa escolha compromete o projeto durante anos. Este capítulo explica por que razão esta série mudou de pacote e instala depois o Filament.

Porque é que este capítulo mudou

As versões anteriores deste tutorial usavam o Voyager. Já não é sustentável.

tcg/voyager está marcado como abandonado no Packagist. A última versão, a 1.8.0, data de setembro de 2024 e declara uma compatibilidade que termina no Laravel 11. Num projeto Laravel 13, o Composer recusa-se simplesmente a instalá-lo: bloqueia primeiro nos avisos de segurança que atingem todas as suas versões, e a restrição ao Laravel 11 faria de qualquer forma falhar a resolução.

É essa a lição útil do episódio: um tutorial assente num pacote mantido por uma só equipa passa a estar errado no dia em que essa equipa para. A escolha do substituto tem, por isso, de pesar a saúde do projeto tanto quanto as suas funcionalidades.

O que foi analisado

Quatro pistas, medidas a 2 de setembro de 2026 sobre os dados públicos do Packagist:

Pacote Última versão Laravel 13 Downloads / mês
tcg/voyager 1.8.0 (set. 2024) não 23 614, mas abandonado
ilsondev/voyager 2.2.1 (jul. 2026) sim 1
orchid/platform 14.53.0 (mar. 2026) sim 27 227
filament/filament 5.7.8 (ago. 2026) sim 3 324 015

O ilsondev/voyager é um fork que funciona: foi instalado num Laravel 13.30.1 acabado de criar para esta revisão, e o dashboard, a ferramenta BREAD, a mediateca, o construtor de menus e as definições respondem todos como devem. Mas soma 12 downloads e uma estrela, e todas as suas versões foram publicadas em três dias de julho de 2026. Puxa ainda o intervention/image na versão 2, cujo ramo já não é o ramo atual. Recomendar este fork a um principiante seria repetir exatamente o erro que partiu este tutorial.

Dito isto, se manténs uma aplicação Voyager já existente e o que procuras é passar para Laravel 13 sem reescrever tudo, este fork é uma ponte honesta. Trata-o como uma etapa, não como um destino.

A escolha: Filament

O Filament ganha por três razões.

  • Não desaparece amanhã. 3,3 milhões de downloads por mês, cem vezes a audiência de todas as alternativas juntas, e versões publicadas todas as semanas.
  • O back-office é código. O Voyager guardava a configuração dos ecrãs na base de dados, clicada numa interface. No Filament, cada ecrã é uma classe PHP versionada com o resto do projeto. Lê-se, compara-se e faz-se o deploy dela como de qualquer outro ficheiro.
  • É uma competência que serve. O Filament é hoje o back-office por omissão do ecossistema Laravel. O que aprendes aqui vais voltar a encontrá-lo num projeto a sério.

Vale a pena conhecer duas alternativas pagas, ainda que não sejam necessárias para este tutorial: o Nova, a solução oficial da equipa Laravel, e o Backpack.

Instalar o Filament

A extensão intl tem de estar presente

O Filament exige a extensão PHP intl. Confirma que a tens antes de instalar:

bash
php -m | grep intl

Se o comando não devolver nada, a instalação falha com esta mensagem, que não menciona o Filament e deixa qualquer um perplexo:

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

O primeiro capítulo já aconselhava a verificá-la por esta razão. Em Debian ou Ubuntu, sudo apt install php8.4-intl. Em macOS com o Homebrew, a extensão já lá está. Em Windows com o WampServer, desmarca e volta a marcar php_intl no menu das extensões PHP. Reinicia depois o servidor.

O pacote e o painel

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

O segundo comando cria app/Providers/Filament/AdminPanelProvider.php, publica os ficheiros CSS e JavaScript do painel em public e esvazia as caches.

code
INFO  Successfully published assets!
INFO  Successfully upgraded!

Criar uma conta

bash
php artisan make:filament-user

O comando pede um nome, um endereço de e-mail e uma palavra-passe, e depois confirma:

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

Vai a http://127.0.0.1:8000/admin e inicia sessão. O dashboard está vazio: é normal, ainda não lhe foi confiado nada. O capítulo 6 vai acrescentar-lhe os artigos, as categorias e as etiquetas.

Controlar quem entra

Em ambiente local, o Filament deixa entrar qualquer utilizador autenticado. Em todo o resto, recusa toda a gente com um erro 403 enquanto o modelo User não disser quem tem direito a entrar. É uma segurança por omissão, mas apanha-te desprevenido: o painel funciona na tua máquina e devolve 403 assim que o site vai para o ar.

A regra declara-se implementando a interface FilamentUser. Mantenha tudo o que o modelo fornecido já contém, os atributos #[Fillable] e #[Hidden] tal como o método casts(): só a interface e o método canAccessPanel() se acrescentam.

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',
        ];
    }
}

Substitui o domínio pelo teu. Esta regra é propositadamente simplista: chega para um projeto de aprendizagem, não para um site público. Um projeto a sério assenta numa coluna is_admin ou num sistema de papéis.

O comportamento verifica-se com dois testes. O segundo conta tanto como o primeiro: em ambiente de teste, o Filament recusa toda a gente enquanto a regra não existir, pelo que um teste que só verificasse a recusa passaria mesmo sem ela.

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 a partir da versão Voyager deste tutorial

Se seguiste este tutorial na sua versão Voyager, eis o que muda.

  • As tabelas do Voyager desaparecem. A instalação criava dez: data_types, data_rows, menus, menu_items, settings, roles, permissions, permission_role, user_roles e translations. Nenhuma serve para alguma coisa. As tuas tabelas de negócio, posts, categories, tags, post_tag, users, ficam tal como estão: o Filament liga-se a elas sem migração de dados.
  • A configuração BREAD perde-se, e é de propósito. O que tinhas clicado no Voyager reescreve-se em PHP, uma vez, nos ficheiros do capítulo 6. É meia hora de trabalho, ao fim da qual a configuração fica versionada.
  • A função setting() deixa de existir. O Voyager fornecia-a para ler o título e a descrição do site. Substitui setting('site.title') por config('app.name'), ou pelas tuas próprias definições.
  • O contorno do slug posts deixa de fazer falta. O Voyager trazia o seu próprio modelo Post, o que obrigava a mudar o slug da tabela para blog-posts e a corrigir um template do pacote. O Filament não impõe modelo nenhum, o conflito desaparece.
  • O endereço do back-office não muda. O Filament instala-se também em /admin.

Para retirar o Voyager de um projeto existente: composer remove tcg/voyager, depois apaga as suas tabelas e a pasta resources/views/vendor/voyager, se existir. Faz antes uma cópia de segurança da base de dados.

O que este capítulo não tratou

Os papéis e permissões finos, a personalização do tema do painel e os widgets do dashboard ultrapassam o âmbito desta série. A documentação do Filament cobre-os.

O capítulo seguinte constrói a página inicial do site público.

Erros frequentes

ext-intl is missing from your system O Filament exige a extensão intl. A mensagem do composer não nomeia o Filament e deixa qualquer um perplexo. Instala o php-intl e volta a lançar a instalação.
O painel devolve 403 fora do ambiente local O Filament recusa toda a gente enquanto o modelo User não implementar FilamentUser::canAccessPanel(). O painel funciona em local e parte assim que o site vai para o ar.
O Composer recusa-se a instalar o tcg/voyager O pacote para no Laravel 11 e está marcado como abandonado. Não existe versão compatível com o Laravel 13.
Newsletter

Os novos testes, tutoriais e projetos, por e-mail.

Testes reproduzíveis, código versionado, resultados datados. Nunca spam.