Filament PHP : construire un back-office en 2h
Filament PHP permet de construire un back-office Laravel complet en quelques heures. Voici comment j'ai créé l'admin de ce blog : articles, catégories, tags, dashboard, le tout sans écrire une ligne de JavaScript.
Quand j'ai voulu créer le back-office de ce blog, j'avais deux options : passer une semaine à coder des CRUDs à la main, ou utiliser Filament. J'ai choisi Filament. Deux heures plus tard, j'avais un admin panel complet avec gestion des articles, catégories, tags, et un dashboard avec des stats.
Filament, c'est un framework Laravel pour construire des interfaces d'administration. Pas un générateur de code — un vrai framework avec des composants, des conventions, et une flexibilité impressionnante quand on en a besoin.
Voici comment construire un back-office de blog fonctionnel en partant de zéro.
Pourquoi Filament
Il existe d'autres options pour les admin panels Laravel : Nova (payant), Backpack (freemium), ou coder à la main. Filament se démarque pour plusieurs raisons :
Open source et gratuit — Pas de licence à payer, même pour un usage commercial.
Basé sur Livewire + Alpine.js — Réactivité sans écrire de JavaScript. Tout reste en PHP.
TALL Stack natif — Tailwind, Alpine, Livewire, Laravel. Si vous êtes déjà dans cet écosystème, vous êtes chez vous.
Extensible — Plugins, composants custom, theming. Vous n'êtes pas enfermé dans une boîte.
Documentation excellente — C'est rare, mais la doc Filament est vraiment bien faite.
Installation
On part d'un projet Laravel frais. Si vous en avez déjà un, passez à l'étape suivante.
composer create-project laravel/laravel blog-filament
cd blog-filamentInstallez Filament :
composer require filament/filament:"^3.2"
php artisan filament:install --panelsCréez un utilisateur admin :
php artisan make:filament-userC'est tout. Lancez le serveur et allez sur /admin :
php artisan serveVous avez un panel d'admin vide, prêt à être rempli.
Les modèles du blog
Avant de créer les ressources Filament, il nous faut les modèles Laravel. Voici la structure classique d'un blog :
php artisan make:model Post -m
php artisan make:model Category -m
php artisan make:model Tag -mMigration Post
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('slug')->unique();
$table->text('excerpt')->nullable();
$table->longText('content');
$table->foreignId('category_id')->nullable()->constrained()->nullOnDelete();
$table->foreignId('author_id')->constrained('users')->cascadeOnDelete();
$table->string('status')->default('draft');
$table->boolean('is_featured')->default(false);
$table->timestamp('published_at')->nullable();
$table->timestamps();
});Migration Category
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->text('description')->nullable();
$table->string('color')->default('#3B82F6');
$table->integer('sort_order')->default(0);
$table->timestamps();
});Migration Tag + pivot
Schema::create('tags', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->timestamps();
});
Schema::create('post_tag', function (Blueprint $table) {
$table->foreignId('post_id')->constrained()->cascadeOnDelete();
$table->foreignId('tag_id')->constrained()->cascadeOnDelete();
$table->primary(['post_id', 'tag_id']);
});Lancez les migrations :
php artisan migrateCréer les Resources Filament
Une "Resource" dans Filament, c'est tout ce qu'il faut pour gérer un modèle : liste, création, édition, suppression. Une commande :
php artisan make:filament-resource Post --generate
php artisan make:filament-resource Category --generate
php artisan make:filament-resource Tag --generateLe flag --generate analyse vos migrations et génère automatiquement les champs du formulaire et les colonnes de la table. Magique.
Allez sur /admin — vous avez déjà trois sections fonctionnelles.

Personnaliser la Resource Post
Le code généré est fonctionnel mais basique. Voici comment l'améliorer.
Ouvrez app/Filament/Resources/PostResource.php :
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Tables;
use Filament\Tables\Table;
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\Section::make('Contenu')
->schema([
Forms\Components\TextInput::make('title')
->required()
->live(onBlur: true)
->afterStateUpdated(fn ($state, $set) =>
$set('slug', Str::slug($state))
),
Forms\Components\TextInput::make('slug')
->required()
->unique(ignoreRecord: true),
Forms\Components\Textarea::make('excerpt')
->rows(3)
->columnSpanFull(),
Forms\Components\RichEditor::make('content')
->required()
->columnSpanFull(),
])
->columns(2),
Forms\Components\Section::make('Métadonnées')
->schema([
Forms\Components\Select::make('category_id')
->relationship('category', 'name')
->searchable()
->preload()
->createOptionForm([
Forms\Components\TextInput::make('name')->required(),
Forms\Components\TextInput::make('slug')->required(),
]),
Forms\Components\Select::make('tags')
->relationship('tags', 'name')
->multiple()
->searchable()
->preload()
->createOptionForm([
Forms\Components\TextInput::make('name')->required(),
Forms\Components\TextInput::make('slug')->required(),
]),
Forms\Components\Select::make('status')
->options([
'draft' => 'Brouillon',
'published' => 'Publié',
])
->default('draft')
->required(),
Forms\Components\Toggle::make('is_featured')
->label('Article mis en avant'),
Forms\Components\DateTimePicker::make('published_at')
->label('Date de publication'),
])
->columns(2),
]);
}Points clés
Génération automatique du slug — Le champ title utilise live(onBlur: true) pour mettre à jour le slug en temps réel.
Création inline — Les selects pour category et tags ont un createOptionForm qui permet de créer une nouvelle catégorie ou un nouveau tag sans quitter le formulaire.
Sections — Les Section organisent visuellement le formulaire. Bien plus lisible qu'une liste plate de champs.
Personnaliser la table
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('title')
->searchable()
->sortable()
->limit(50),
Tables\Columns\TextColumn::make('category.name')
->badge()
->color(fn ($record) => Color::hex($record->category?->color ?? '#gray')),
Tables\Columns\TextColumn::make('status')
->badge()
->color(fn (string $state): string => match ($state) {
'draft' => 'gray',
'published' => 'success',
}),
Tables\Columns\IconColumn::make('is_featured')
->boolean(),
Tables\Columns\TextColumn::make('published_at')
->dateTime('d/m/Y H:i')
->sortable(),
])
->filters([
Tables\Filters\SelectFilter::make('status')
->options([
'draft' => 'Brouillon',
'published' => 'Publié',
]),
Tables\Filters\SelectFilter::make('category')
->relationship('category', 'name'),
Tables\Filters\TernaryFilter::make('is_featured')
->label('Mis en avant'),
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
])
->defaultSort('created_at', 'desc');
}En quelques lignes : recherche, tri, filtres, badges colorés, actions. Tout est déclaratif.
Ajouter un Dashboard
Le dashboard par défaut est vide. Ajoutons des widgets utiles.
Créez un widget :
php artisan make:filament-widget StatsOverview --stats-overviewÉditez app/Filament/Widgets/StatsOverview.php :
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
use Filament\Widgets\StatsOverviewWidget\Stat;
use App\Models\Post;
class StatsOverview extends BaseWidget
{
protected function getStats(): array
{
return [
Stat::make('Articles publiés', Post::where('status', 'published')->count())
->description('Total des articles en ligne')
->color('success'),
Stat::make('Brouillons', Post::where('status', 'draft')->count())
->description('En attente de publication')
->color('warning'),
Stat::make('Articles ce mois', Post::whereMonth('published_at', now()->month)->count())
->description('Publications du mois en cours')
->color('info'),
];
}
}Le widget apparaît automatiquement sur le dashboard.

Prompt : "A dashboard with three statistics cards showing numbers and trends, plus a chart widget below, modern admin interface, clean white background with subtle shadows, purple accent color, professional design, 16:9"
Bonus : Actions personnalisées
Ajoutons un bouton "Publier" qui change le statut en un clic :
Tables\Actions\Action::make('publish')
->label('Publier')
->icon('heroicon-o-check-circle')
->color('success')
->visible(fn ($record) => $record->status === 'draft')
->requiresConfirmation()
->action(fn ($record) => $record->update([
'status' => 'published',
'published_at' => now(),
]))Ajoutez ça dans le tableau actions de votre table. Un bouton "Publier" apparaît sur chaque brouillon.
Ce qui m'a pris 2 heures
Pour être précis, voici ce que j'ai construit en 2 heures pour ce blog :
- 3 Resources complètes (Posts, Categories, Tags)
- Formulaires avec RichEditor, relations, création inline
- Tables avec recherche, filtres, tri, pagination
- Dashboard avec 4 widgets de stats
- Actions custom (publier, dépublier, mettre en avant)
- Soft deletes avec corbeille
Ce qui aurait pris une journée à coder from scratch.
Les limites
Filament n'est pas parfait :
Courbe d'apprentissage — La doc est bonne, mais il y a beaucoup de concepts. Les premières heures sont un peu déroutantes.
Performance — Livewire ajoute de l'overhead. Pour un admin panel classique, c'est invisible. Pour des tables avec 100k lignes et des calculs complexes, ça peut se sentir.
Personnalisation poussée — Tant que vous restez dans les conventions, tout est fluide. Dès que vous voulez un comportement très custom, il faut comprendre comment Livewire et les composants Filament fonctionnent en interne.
Mises à jour — Filament évolue vite. Les breaking changes entre versions majeures peuvent demander du travail de migration.
Verdict
Pour un back-office standard (CRUD, relations, dashboard), Filament est imbattable. Le ratio temps investi / résultat est exceptionnel.
Je l'utilise maintenant pour tous mes projets Laravel qui ont besoin d'une interface d'admin. Ce blog, des outils internes, des MVPs clients. À chaque fois, je gagne des jours de développement.
Si vous êtes sur Laravel et que vous codez encore vos admins à la main, essayez Filament sur un petit projet. Vous ne reviendrez probablement pas en arrière.
Vous utilisez Filament ?
Ressources
Articles similaires
MCP : le protocole qui connecte l'IA au monde réel
Le Model Context Protocol (MCP) est devenu le standard universel pour connecter les IA à vos outils. Adopté par Anthropic, OpenAI, Google et Microsoft, il change la façon dont on construit des applications IA. Guide complet et pratique.