lbcdev/filament-map-field
Filament Map Field
Un paquete de Filament que proporciona componentes de campo de mapa para formularios e infolists, utilizando el componente Livewire lbcdev-map.
✨ Compatible con Filament v3 y v4 - Actualiza sin preocupaciones, sin cambios en tu código.
✨ Características
- 🗺️ MapField para formularios Filament (selección de puntos interactiva)
- 📋 MapEntry para infolists Filament (visualización de puntos)
- 📐 MapBoundsField para formularios Filament (selección de áreas rectangulares)
- 📊 MapBoundsEntry para infolists Filament (visualización de áreas)
- 🎯 Integración perfecta con el componente Livewire lbcdev-map
- 📍 Soporte para campos de latitud/longitud separados
- 🔄 Soporte para campos JSON anidados (v1.1.0+) - Usa notación de punto:
'ubicacion.latitud' - ⚡ Actualización reactiva de coordenadas
- 🎨 Compatible con el tema de Filament
- 🔧 Altamente configurable
- ✨ Compatible con Filament v3 y v4 - Sin cambios necesarios al actualizar
📋 Requisitos
- PHP 8.1+ (PHP 8.2+ recomendado para Filament v4)
- Laravel 10.x, 11.x o 12.x
- Filament 3.x o 4.x ✨
- Livewire 3.x
- lbcdev/livewire-map-component ^1.0
Nota: Este paquete es compatible con Filament v3 y v4. No necesitas hacer cambios en tu código al actualizar de Filament v3 a v4.
📦 Instalación
1. Instalar el paquete via Composer
composer require lbcdev/filament-map-field
2. Incluir Leaflet.js en tu layout
El paquete depende de lbcdev/livewire-map-component, que requiere Leaflet.js. Agrega estos scripts en el <head> de tu layout principal:
<!-- Leaflet CSS -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<!-- Leaflet JS -->
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<!-- Leaflet Draw (solo si usas MapBoundsField) -->
<link rel="stylesheet" href="https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.css" />
<script src="https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.js"></script>
Nota: Leaflet Draw solo es necesario si vas a usar
MapBoundsFieldpara seleccionar áreas rectangulares. Si solo usasMapFieldpara puntos, no es necesario incluirlo.
Con Filament v4 puedes usar un hook para incluir los tags de Leaflet. Agrega el siguiente código a tu archivo app/Providers/Filament/AdminPanelProvider.php:
public function panel(Panel $panel): Panel{
return $panel
...
->renderHook(
'panels::head.end',
fn(): string => view('filament.hooks.leaflet-assets')->render()
)
...
}
3. (Opcional) Publicar las vistas
Si deseas personalizar las vistas del componente:
php artisan vendor:publish --tag=filament-map-field-views
Las vistas se publicarán en resources/views/vendor/filament-map-field/.
🚀 Uso
MapField en Formularios
El componente MapField permite a los usuarios seleccionar coordenadas de forma interactiva en un formulario.
Uso básico
use Lbcdev\FilamentMapField\Forms\Components\MapField;
MapField::make('location')
->latitude('latitude')
->longitude('longitude');
Con todas las opciones
MapField::make('location')
->latitude('latitude') // Campo donde se guardará la latitud
->longitude('longitude') // Campo donde se guardará la longitud
->height(500) // Altura del mapa en píxeles (default: 400)
->zoom(15) // Nivel de zoom inicial (default: 15)
->showPasteButton() // Mostrar botón para pegar coordenadas
->showLabel() // Mostrar etiqueta con coordenadas
->interactive(); // Permitir interacción (default: true)
Modo de solo lectura
// Usando readOnly() - Compatible con la API estándar de Filament
MapField::make('location')
->latitude('latitude')
->longitude('longitude')
->readOnly();
// O usando interactive(false) - Mismo resultado
MapField::make('location')
->latitude('latitude')
->longitude('longitude')
->interactive(false);
Ejemplo completo en un Resource
<?php
namespace App\Filament\Resources;
use App\Models\Location;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Lbcdev\FilamentMapField\Forms\Components\MapField;
class LocationResource extends Resource
{
protected static ?string $model = Location::class;
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('name')
->required()
->maxLength(255),
Forms\Components\TextInput::make('address')
->maxLength(255),
Forms\Components\Grid::make(2)
->schema([
Forms\Components\TextInput::make('latitude')
->numeric()
->required(),
Forms\Components\TextInput::make('longitude')
->numeric()
->required(),
]),
MapField::make('map')
->latitude('latitude')
->longitude('longitude')
->height(500)
->zoom(15)
->showPasteButton()
->columnSpanFull(),
]);
}
}
⚠️ Importante: Uso con Notación de Punto (JSON)
Cuando uses notación de punto para campos JSON anidados, el primer parámetro de make() debe coincidir con el campo padre:
// ✅ CORRECTO: make() usa el campo padre 'ubicacion'
MapField::make('ubicacion')
->latitude('ubicacion.latitud')
->longitude('ubicacion.longitud')
->columnSpanFull();
// ❌ INCORRECTO: make() usa 'map' pero los campos son 'ubicacion.latitud'
// Esto causará error "The ubicación field is required" en modo create
MapField::make('map')
->latitude('ubicacion.latitud')
->longitude('ubicacion.longitud')
->columnSpanFull();
Modelo con campo JSON:
class Store extends Model
{
protected $fillable = ['name', 'ubicacion'];
protected $casts = [
'ubicacion' => 'array', // Campo JSON
];
}
Migración:
Schema::create('stores', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->json('ubicacion')->nullable(); // Campo JSON
$table->timestamps();
});
MapEntry en Infolists
El componente MapEntry muestra las coordenadas en un mapa de solo lectura en infolists.
Uso básico de MapEntry
use Lbcdev\FilamentMapField\Infolists\Entries\MapEntry;
MapEntry::make('location')
->latitude('latitude')
->longitude('longitude');
Con opciones
MapEntry::make('location')
->latitude('latitude')
->longitude('longitude')
->height(400)
->zoom(15)
->showLabel();
Ejemplo completo de un Resource
<?php
namespace App\Filament\Resources;
use App\Models\Location;
use Filament\Infolists;
use Filament\Infolists\Infolist;
use Filament\Resources\Resource;
use Lbcdev\FilamentMapField\Infolists\Entries\MapEntry;
class LocationResource extends Resource
{
protected static ?string $model = Location::class;
public static function infolist(Infolist $infolist): Infolist
{
return $infolist
->schema([
Infolists\Components\TextEntry::make('name'),
Infolists\Components\TextEntry::make('address'),
Infolists\Components\Grid::make(2)
->schema([
Infolists\Components\TextEntry::make('latitude')
->numeric(decimalPlaces: 6),
Infolists\Components\TextEntry::make('longitude')
->numeric(decimalPlaces: 6),
]),
MapEntry::make('map')
->latitude('latitude')
->longitude('longitude')
->height(400)
->zoom(15)
->columnSpanFull(),
]);
}
}
MapBoundsField en Formularios
El componente MapBoundsField permite a los usuarios seleccionar áreas rectangulares de forma interactiva en un formulario.
Uso básico
use Lbcdev\FilamentMapField\Forms\Components\MapBoundsField;
MapBoundsField::make('area')
->southWestLat('sw_lat')
->southWestLng('sw_lng')
->northEastLat('ne_lat')
->northEastLng('ne_lng');
Con todas las opciones
MapBoundsField::make('area')
->southWestLat('sw_lat') // Campo para latitud suroeste
->southWestLng('sw_lng') // Campo para longitud suroeste
->northEastLat('ne_lat') // Campo para latitud noreste
->northEastLng('ne_lng') // Campo para longitud noreste
->height(500) // Altura del mapa en píxeles
->zoom(13) // Nivel de zoom inicial
->showLabel() // Mostrar etiqueta con coordenadas
->defaultCenter(40.4168, -3.7038); // Centro por defecto (Madrid)
Con campos JSON anidados
// ✅ CORRECTO: make() usa el campo padre 'bounds'
MapBoundsField::make('bounds')
->southWestLat('bounds.sw_lat')
->southWestLng('bounds.sw_lng')
->northEastLat('bounds.ne_lat')
->northEastLng('bounds.ne_lng')
->height(500)
->zoom(13);
Nota: Al igual que con
MapField, cuando uses notación de punto, el primer parámetro demake()debe coincidir con el campo padre JSON. Ver la sección "⚠️ Importante: Uso con Notación de Punto" arriba para más detalles.
MapBoundsEntry en Infolists
El componente MapBoundsEntry muestra áreas rectangulares en un mapa de solo lectura en infolists.
Uso básico
use Lbcdev\FilamentMapField\Infolists\Entries\MapBoundsEntry;
MapBoundsEntry::make('area')
->southWestLat('sw_lat')
->southWestLng('sw_lng')
->northEastLat('ne_lat')
->northEastLng('ne_lng');
Con opciones
MapBoundsEntry::make('area')
->southWestLat('sw_lat')
->southWestLng('sw_lng')
->northEastLat('ne_lat')
->northEastLng('ne_lng')
->height(400)
->zoom(13)
->showLabel();
🎨 Métodos Disponibles
MapField (Forms)
| Método | Descripción | Default |
|---|---|---|
latitude(string $field) |
Campo donde se guardará la latitud. Soporta notación de punto para JSON: 'ubicacion.latitud' |
null |
longitude(string $field) |
Campo donde se guardará la longitud. Soporta notación de punto para JSON: 'ubicacion.longitud' |
null |
height(int $height) |
Altura del mapa en píxeles | 400 |
zoom(int $zoom) |
Nivel de zoom inicial (1-20) | 15 |
showPasteButton(bool $show = true) |
Mostrar botón para pegar coordenadas | false |
showLabel(bool $show = true) |
Mostrar etiqueta con coordenadas | true |
interactive(bool $interactive = true) |
Permitir interacción con el mapa | true |
readOnly(bool $condition = true) |
Hacer el mapa de solo lectura (alias de interactive(false)) |
false |
MapEntry (Infolists)
| Método | Descripción | Default |
|---|---|---|
latitude(string $field) |
Campo de donde leer la latitud. Soporta notación de punto para JSON: 'ubicacion.latitud' |
null |
longitude(string $field) |
Campo de donde leer la longitud. Soporta notación de punto para JSON: 'ubicacion.longitud' |
null |
height(int $height) |
Altura del mapa en píxeles | 300 |
zoom(int $zoom) |
Nivel de zoom inicial (1-20) | 15 |
showLabel(bool $show = true) |
Mostrar etiqueta con coordenadas | true |
MapBoundsField (Forms)
| Método | Descripción | Default |
|---|---|---|
southWestLat(string $field) |
Campo para latitud suroeste. Soporta notación de punto: 'bounds.sw_lat' |
null |
southWestLng(string $field) |
Campo para longitud suroeste. Soporta notación de punto: 'bounds.sw_lng' |
null |
northEastLat(string $field) |
Campo para latitud noreste. Soporta notación de punto: 'bounds.ne_lat' |
null |
northEastLng(string $field) |
Campo para longitud noreste. Soporta notación de punto: 'bounds.ne_lng' |
null |
height(int $height) |
Altura del mapa en píxeles | 400 |
zoom(int $zoom) |
Nivel de zoom inicial (1-20) | 13 |
showLabel(bool $show = true) |
Mostrar etiqueta con coordenadas de los límites | true |
defaultCenter(float $lat, float $lng) |
Centro por defecto del mapa | [36.9990019, -6.5478919] |
MapBoundsEntry (Infolists)
| Método | Descripción | Default |
|---|---|---|
southWestLat(string $field) |
Campo de donde leer latitud suroeste. Soporta notación de punto: 'bounds.sw_lat' |
null |
southWestLng(string $field) |
Campo de donde leer longitud suroeste. Soporta notación de punto: 'bounds.sw_lng' |
null |
northEastLat(string $field) |
Campo de donde leer latitud noreste. Soporta notación de punto: 'bounds.ne_lat' |
null |
northEastLng(string $field) |
Campo de donde leer longitud noreste. Soporta notación de punto: 'bounds.ne_lng' |
null |
height(int $height) |
Altura del mapa en píxeles | 300 |
zoom(int $zoom) |
Nivel de zoom inicial (1-20) | 13 |
showLabel(bool $show = true) |
Mostrar etiqueta con coordenadas de los límites | true |
💡 Ejemplos Avanzados
Formulario con validación
Los componentes MapField y MapBoundsField soportan el método ->required() de forma nativa. Cuando se marca un campo como requerido, automáticamente valida que todos los campos anidados (latitud, longitud, límites) tengan valores.
// Ejemplo 1: MapField con validación requerida
MapField::make('ubicacion')
->latitude('ubicacion.latitud')
->longitude('ubicacion.longitud')
->height(500)
->zoom(15)
->showPasteButton()
->required() // ✅ Valida que latitud y longitud tengan valores
->label('Ubicación'),
// Ejemplo 2: MapBoundsField con validación requerida
MapBoundsField::make('limites')
->southWestLat('limites.latitud_min')
->southWestLng('limites.longitud_min')
->northEastLat('limites.latitud_max')
->northEastLng('limites.longitud_max')
->height(500)
->zoom(13)
->required() // ✅ Valida que todos los límites tengan valores
->label('Límites del área'),
// Ejemplo 3: Validación con campos separados (modo tradicional)
Forms\Components\Grid::make(2)
->schema([
Forms\Components\TextInput::make('latitude')
->numeric()
->required()
->minValue(-90)
->maxValue(90)
->step(0.000001),
Forms\Components\TextInput::make('longitude')
->numeric()
->required()
->minValue(-180)
->maxValue(180)
->step(0.000001),
]),
MapField::make('map')
->latitude('latitude')
->longitude('longitude')
->height(600)
->zoom(12)
->showPasteButton()
->columnSpanFull(),
Nota: El método
->required()funciona tanto en modo Create como Edit. La validación se aplica automáticamente a los campos anidados configurados con notación de punto.
Múltiples mapas en un formulario
Forms\Components\Tabs::make('Locations')
->tabs([
Forms\Components\Tabs\Tab::make('Origen')
->schema([
Forms\Components\Grid::make(2)
->schema([
Forms\Components\TextInput::make('origin_latitude')
->numeric()
->required(),
Forms\Components\TextInput::make('origin_longitude')
->numeric()
->required(),
]),
MapField::make('origin_map')
->latitude('origin_latitude')
->longitude('origin_longitude')
->height(400)
->showPasteButton(),
]),
Forms\Components\Tabs\Tab::make('Destino')
->schema([
Forms\Components\Grid::make(2)
->schema([
Forms\Components\TextInput::make('destination_latitude')
->numeric()
->required(),
Forms\Components\TextInput::make('destination_longitude')
->numeric()
->required(),
]),
MapField::make('destination_map')
->latitude('destination_latitude')
->longitude('destination_longitude')
->height(400)
->showPasteButton(),
]),
]),
Campos JSON anidados (v1.1.0+)
El paquete soporta guardar coordenadas en campos JSON anidados usando notación de punto. Esto es útil cuando quieres almacenar las coordenadas en una estructura JSON en lugar de campos separados.
Modo 1: Campos separados (tradicional)
// Migración
Schema::create('locations', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->decimal('latitude', 10, 8)->nullable();
$table->decimal('longitude', 11, 8)->nullable();
});
// Formulario
MapField::make('map')
->latitude('latitude')
->longitude('longitude');
// Resultado en BD:
// latitude: 40.416775
// longitude: -3.703790
Modo 2: Campo JSON anidado (nuevo)
// Migración
Schema::create('locations', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->json('ubicacion')->nullable();
});
// Modelo
class Location extends Model
{
protected $casts = [
'ubicacion' => 'array',
];
}
// Formulario
MapField::make('ubicacion')
->latitude('ubicacion.latitud')
->longitude('ubicacion.longitud')
->height(500)
->zoom(15)
->showPasteButton();
// Resultado en BD (campo JSON):
// ubicacion: {"latitud": "40.416775", "longitud": "-3.703790"}
Ventajas del modo JSON
- ✅ Agrupa coordenadas relacionadas en un solo campo
- ✅ Facilita la gestión de múltiples ubicaciones
- ✅ Permite nombres de campos personalizados (latitud/longitud, lat/lng, etc.)
- ✅ 100% retrocompatible con el modo tradicional
Ejemplo completo con JSON
<?php
namespace App\Filament\Resources;
use App\Models\Store;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Lbcdev\FilamentMapField\Forms\Components\MapField;
class StoreResource extends Resource
{
protected static ?string $model = Store::class;
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('name')
->required()
->maxLength(255),
Forms\Components\TextInput::make('address')
->maxLength(255),
MapField::make('ubicacion')
->label('Ubicación en el mapa')
->latitude('ubicacion.latitud')
->longitude('ubicacion.longitud')
->height(500)
->zoom(15)
->showPasteButton()
->columnSpanFull(),
]);
}
}
🔧 Personalización
Publicar y personalizar vistas
php artisan vendor:publish --tag=filament-map-field-views
Las vistas estarán disponibles en:
resources/views/vendor/filament-map-field/forms/components/map-field.blade.phpresources/views/vendor/filament-map-field/infolists/entries/map-entry.blade.php
🔄 Compatibilidad con Filament v3 y v4
Este paquete es totalmente compatible con Filament v3 y v4 sin necesidad de cambios en tu código.
¿Qué significa esto?
- ✅ Puedes usar este paquete con Filament v3
- ✅ Puedes usar este paquete con Filament v4
- ✅ Al actualizar de Filament v3 a v4, no necesitas cambiar nada en el código que usa este paquete
- ✅ El paquete detecta automáticamente la versión de Filament y se adapta
Requisitos según la versión de Filament
Para Filament v3
- PHP 8.1+
- Laravel 10.x o 11.x
- Tailwind CSS 3.x (si usas tema personalizado)
Para Filament v4
- PHP 8.2+
- Laravel 11.28+ o 12.x
- Tailwind CSS 4.x (si usas tema personalizado)
Actualización de Filament v3 a v4
Si estás actualizando tu proyecto de Filament v3 a v4:
-
Actualiza Filament siguiendo la guía oficial de actualización
-
Actualiza las dependencias:
composer update -
¡Listo! El paquete
filament-map-fieldseguirá funcionando sin cambios
No necesitas:
- ❌ Cambiar el código de tus Resources
- ❌ Modificar las llamadas a
MapFieldoMapEntry - ❌ Actualizar la sintaxis del paquete
Nota sobre Tailwind CSS
Si usas un tema personalizado en Filament, necesitarás actualizar Tailwind CSS de v3 a v4 al migrar a Filament v4. Esto es un requisito de Filament, no de este paquete específicamente.
Consulta la guía de actualización de Tailwind CSS v4 para más detalles.
🔄 Actualización del Paquete
Actualizar a una versión específica
Para actualizar el paquete a una versión específica usando tags de GitHub:
# Actualizar a la última versión
composer update lbcdev/filament-map-field
# O instalar una versión específica por tag
composer require lbcdev/filament-map-field:1.0.0
Usar una versión específica en composer.json
Puedes especificar la versión exacta en tu composer.json:
{
"require": {
"lbcdev/filament-map-field": "^1.0"
}
}
O usar un tag específico:
{
"require": {
"lbcdev/filament-map-field": "1.0.0"
}
}
Verificar la versión instalada
composer show lbcdev/filament-map-field
Limpiar caché después de actualizar
Después de actualizar, es recomendable limpiar las cachés:
php artisan filament:cache-components
php artisan view:clear
php artisan cache:clear
🐛 Solución de Problemas
Si encuentras problemas al usar el paquete, consulta la Guía de Solución de Problemas que incluye:
- ✅ El mapa no actualiza los campos del formulario
- ✅ El mapa no se muestra
- ✅ Problemas de estilos
- ✅ Errores comunes y sus soluciones
🤝 Créditos
Este paquete utiliza:
- lbcdev/livewire-map-component - Componente Livewire de mapas
- Leaflet.js - Biblioteca de mapas interactivos
- Filament - Framework de administración para Laravel
📄 Licencia
Este paquete es software de código abierto licenciado bajo la Licencia MIT.
🐛 Soporte
Si encuentras algún problema o tienes sugerencias:
👨💻 Autor
Desarrollado por Luinux81