Filament : composants et actions personnalisés

Filament offre une base solide pour les back-offices Laravel. Mais la vraie puissance émerge quand on sait l'étendre. Voyons comment créer des composants de formulaire, des actions personnalisées et des widgets sur mesure.

Architecture de Filament

Filament repose sur Livewire et utilise un système de composants chaînables. Chaque élément (champ de formulaire, colonne de table, action) est un objet PHP configurable via des méthodes fluent. Cette architecture permet d'étendre facilement chaque partie.

Composants clés

Forms : champs et layouts pour la saisie de données
Tables : colonnes, filtres et actions pour l'affichage
Actions : boutons déclenchant des opérations
Widgets : blocs d'information sur le dashboard

Créer un champ de formulaire custom

Les champs natifs de Filament couvrent 90% des besoins. Pour le reste, créez vos propres composants. Exemple : un sélecteur de couleur avec preview en temps réel.

app/Forms/Components/ColorPicker.php
namespace App\Forms\Components; use Filament\Forms\Components\Field; class ColorPicker extends Field { protected string $view = 'forms.components.color-picker'; protected array $presetColors = []; public function presets(array $colors): static { $this->presetColors = $colors; return $this; } public function getPresetColors(): array { return $this->presetColors; } }

La vue Blade associee

resources/views/forms/components/color-picker.blade.php
<x-dynamic-component :component="$getFieldWrapperView()" :field="$field" > <div x-data="{ color: $wire.entangle('{{ $getStatePath() }}') }"> <!-- Input couleur natif --> <input type="color" x-model="color" class="w-full h-10 rounded-lg cursor-pointer" /> <!-- Presets --> @if(count($getPresetColors()) > 0) <div class="flex gap-2 mt-2"> @foreach($getPresetColors() as $preset) <button type="button" x-on:click="color = '{{ $preset }}'" class="w-8 h-8 rounded-full border-2" style="background: {{ $preset }}" ></button> @endforeach </div> @endif </div> </x-dynamic-component>

Utilisation dans une ressource

app/Filament/Resources/ProductResource.php
use App\Forms\Components\ColorPicker; public static function form(Form $form): Form { return $form->schema([ ColorPicker::make('color') ->label('Couleur du produit') ->presets(['#FF5733', '#33FF57', '#3357FF', '#000000']) ->required(), ]); }

Actions personnalisées

Les actions sont des opérations déclenchées par l'utilisateur. Filament propose des actions de table, de page et de formulaire. Créez les vôtres pour des workflows métier spécifiques.

Action de table avec confirmation

app/Filament/Resources/OrderResource.php
use Filament\Tables\Actions\Action; use Filament\Support\Icons\Heroicon; public static function table(Table $table): Table { return $table ->actions([ Action::make('markAsShipped') ->label('Marquer expedie') ->icon(Heroicon::Truck) ->color('success') ->requiresConfirmation() ->modalHeading('Confirmer l\'expedition') ->modalDescription('Le client recevra une notification.') ->visible(fn ($record) => $record->status === 'pending') ->action(function (Order $record) { $record->update(['status' => 'shipped']); $record->notify(new OrderShippedNotification()); Notification::make() ->success() ->title('Commande expediee') ->send(); }), ]); }

Action avec formulaire modal

Pour des actions necessitant une saisie utilisateur, ajoutez un formulaire dans la modal.

Actions avec formulaire
Action::make('addNote') ->label('Ajouter une note') ->icon(Heroicon::PencilSquare) ->form([ Textarea::make('content') ->label('Note') ->required() ->rows(3), Toggle::make('is_internal') ->label('Note interne (non visible client)') ->default(true), ]) ->action(function (Order $record, array $data) { $record->notes()->create([ 'content' => $data['content'], 'is_internal' => $data['is_internal'], 'user_id' => auth()->id(), ]); }),

Actions bulk

Utilisez BulkAction pour les opérations sur plusieurs enregistrements. La méthode action() reçoit une Collection au lieu d'un record unique.

Widgets dashboard personnalisés

Les widgets permettent d'afficher des KPIs, graphiques ou informations résumées sur le dashboard. Filament supporte les stats, charts et widgets custom.

app/Filament/Widgets/RevenueChart.php
namespace App\Filament\Widgets; use Filament\Widgets\ChartWidget; class RevenueChart extends ChartWidget { protected static ?string $heading = 'Chiffre d\'affaires'; protected static ?int $sort = 2; protected function getData(): array { $data = Order::query() ->where('status', 'completed') ->whereBetween('created_at', [ now()->subMonths(6), now(), ]) ->selectRaw('DATE_FORMAT(created_at, "%Y-%m") as month') ->selectRaw('SUM(total) as revenue') ->groupBy('month') ->orderBy('month') ->get(); return [ 'datasets' => [ [ 'label' => 'CA (€)', 'data' => $data->pluck('revenue')->toArray(), 'backgroundColor' => 'rgba(253, 174, 75, 0.5)', 'borderColor' => '#fdae4b', ], ], 'labels' => $data->pluck('month')->toArray(), ]; } protected function getType(): string { return 'bar'; } }

Widget avec filtres interactifs

Widget avec filtre periode
class OrdersOverview extends StatsOverviewWidget { protected function getStats(): array { return [ Stat::make('Commandes', Order::count()) ->description('Total commandes') ->descriptionIcon('heroicon-m-shopping-cart') ->chart($this->getOrdersChartData()) ->color('success'), Stat::make('CA', number_format(Order::sum('total'), 2) . ' €') ->description($this->getRevenueChange()) ->descriptionIcon($this->getRevenueIcon()) ->color($this->getRevenueColor()), Stat::make('Panier moyen', number_format(Order::avg('total'), 2) . ' €') ->description('Valeur moyenne'), ]; } protected function getRevenueChange(): string { $current = Order::whereMonth('created_at', now()->month)->sum('total'); $previous = Order::whereMonth('created_at', now()->subMonth()->month)->sum('total'); if ($previous == 0) return 'N/A'; $change = (($current - $previous) / $previous) * 100; return sprintf('%+.1f%% vs mois dernier', $change); } }

Étendre les colonnes de table

Créez des colonnes personnalisées pour des affichages spécifiques non couverts par les colonnes natives.

app/Tables/Columns/StatusBadge.php
namespace App\Tables\Columns; use Filament\Tables\Columns\Column; class StatusBadge extends Column { protected string $view = 'tables.columns.status-badge'; protected array $statusColors = [ 'pending' => 'warning', 'processing' => 'info', 'completed' => 'success', 'cancelled' => 'danger', ]; public function getColor(): string { $value = $this->getState(); return $this->statusColors[$value] ?? 'gray'; } }

Performance

Évitez les requêtes N+1 dans les colonnes custom. Utilisez $table->query() pour eager loader les relations nécessaires, ou le callback getStateUsing() avec des données pré-chargées.

Relation Managers avancés

Les Relation Managers permettent de gérer les relations directement depuis la page d'édition d'une ressource. Personnalisez-les pour des workflows spécifiques.

app/Filament/Resources/CustomerResource/RelationManagers/OrdersRelationManager.php
class OrdersRelationManager extends RelationManager { protected static string $relationship = 'orders'; public function table(Table $table): Table { return $table ->columns([ TextColumn::make('reference')->searchable(), TextColumn::make('total')->money('EUR'), TextColumn::make('status')->badge()->color( fn (string $state): string => match ($state) { 'pending' => 'warning', 'completed' => 'success', default => 'gray', } ), ]) ->headerActions([ // Action pour créer une commande avec valeurs pré-remplies CreateAction::make() ->mutateFormDataUsing(function (array $data): array { $data['reference'] = 'ORD-' . strtoupper(Str::random(8)); return $data; }), ]) ->actions([ ActionGroup::make([ ViewAction::make(), EditAction::make(), Action::make('duplicate') ->action(fn ($record) => $record->replicate()->save()), ]), ]); } }

Bonnes pratiques

Filament n'est pas qu'un CRUD generator. C'est un framework complet pour construire des interfaces d'administration. Maîtriser ses mécanismes d'extension permet de créer des back-offices sur mesure sans sacrifier la maintenabilité.

Ressources

Besoin d'aide sur votre projet ?

Je peux vous accompagner dans le développement ou l'optimisation de votre application.

Me contacter