Créer un champ personnalisé Filament pour rechercher des adresses avec OpenStreetMap

April 18, 2025 Tutoriel

Découvrez comment créer un champ personnalisé Filament qui permet à vos utilisateurs de rechercher une adresse via l’API OpenStreetMap

Créer un champ personnalisé Filament pour rechercher des adresses avec OpenStreetMap

Dans ce tutoriel, nous allons créer un champ personnalisé Filament v3 qui permet à l’utilisateur de rechercher une adresse à l’aide de l’API OpenStreetMap (Nominatim). Lorsqu’une adresse est sélectionnée, le champ mettra à jour automatiquement le champ address.


🛠️ Étape 1 – Générer le champ personnalisé

Filament fournit une commande artisan pour créer un champ :

php artisan make:filament-custom-field AddressSearch

Cela crée deux fichiers :

  • app/Forms/Components/AddressSearch.php
  • resources/views/forms/components/address-search.blade.php

🎨 Étape 2 – Créer l’interface avec Alpine.js

Dans resources/views/forms/components/address-search.blade.php, utilisez le composant dynamique de Filament :

<x-dynamic-component
    :component="$getFieldWrapperView()"
    :field="$field"
>
    <div
        x-data="{
            search: '',
            results: [],
            state: $wire.$entangle('{{ $getStatePath() }}').defer,

            async searchAddress() {
                if (this.search.length < 3) {
                    this.results = []
                    return
                }

                const response = await fetch(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(this.search)}`)
                const data = await response.json()

                this.results = data.map(item => ({
                    label: item.display_name,
                    value: {
                        address: item.display_name,
                        latitude: item.lat,
                        longitude: item.lon,
                    }
                })).slice(0, 5)
            },

            selectResult(result) {
                this.state = result.value
                this.search = result.label
                this.results = []
            }
        }"
        class="space-y-2"
    >
        <input
            type="text"
            class="w-full border rounded px-3 py-2"
            placeholder="Rechercher une adresse..."
            x-model="search"
            x-on:input.debounce.500ms="searchAddress"
        />

        <ul
            class="border rounded bg-white max-h-60 overflow-y-auto shadow"
            x-show="results.length > 0"
        >
            <template x-for="result in results" :key="result.label">
                <li
                    x-text="result.label"
                    @click="selectResult(result)"
                    class="px-4 py-2 hover:bg-gray-100 cursor-pointer"
                ></li>
            </template>
        </ul>
    </div>
</x-dynamic-component>

Ce champ :

  • Utilise Alpine.js pour interagir avec l'utilisateur.
  • Fait une requête vers OpenStreetMap après 500ms de frappe.
  • Affiche une liste déroulante avec les résultats.
  • Met à jour automatiquement la valeur du champ (et les autres champs via le backend).

📆 Étape 4 – Utiliser le champ dans un formulaire Filament

Dans votre PlaceResource::form() :

use App\Forms\Components\AddressSearch;
use Filament\Forms\Components\TextInput;

public static function form(Form $form): Form
{
    return $form
        ->schema([
            AddressSearch::make('location'), // champ principal contenant l’objet sélectionné
        ]);
}

✅ Résultat

  • Champ avec recherche en temps réel sur OpenStreetMap.
  • Intégration native dans Filament.
  • Synchronisation automatique avec le modèle Laravel.
  • Peut être réutilisé facilement dans d'autres formulaires.

🛍️ Aller plus loin

  • Ajouter une carte (Leaflet.js) pour visualiser l’adresse sélectionnée.
  • Limiter les résultats à un pays ou une ville spécifique.
  • Ajouter un fallback ou gestion des erreurs API.

Et voilà ! Vous avez maintenant un champ personnalisable, moderne et réactif, directement connecté à une source ouverte de données d’adresses 🎉