whitecube/nova-google-maps
Add an interactive Google Maps picker to Laravel Nova. This field lets users search addresses with autocomplete, drag a marker to fine-tune coordinates, and save latitude/longitude or formatted address data directly in your Nova resources. Configurable map options make it easy to fit.
composer why-not whitecube/nova-google-maps if resolution fails. On Nova 4/5 you will almost certainly need a fork.composer require whitecube/nova-google-maps
php artisan vendor:publish --tag=nova-google-maps # if the tag exists in your version
.env and cache config.use Whitecube\NovaGoogleMaps\GoogleMaps;
public function fields(Request $request)
{
return [
GoogleMaps::make('Location'),
];
}
$table->json('location')->nullable();
and cast it on the model:
protected $casts = ['location' => 'array'];
Resource + cast + migration is the whole contract. Treat the field as a JSON-backed composite value. Everything else (rendering, distance queries, display) is derived from the stored array.
Normalising accessors. Don't reach into the raw JSON in views. Add helpers:
public function getLatAttribute() { return $this->location['lat'] ?? null; }
public function getLngAttribute() { return $this->location['lng'] ?? null; }
public function getLocationLabelAttribute() { return $this->location['address'] ?? $this->location['name'] ?? null; }
Search-to-pin workflow. The intended UX is: type an address → Algolia Places returns candidates → selecting one drops the marker and fills the payload. When search is unavailable (see Gotchas), the manual-drag fallback still yields usable coordinates — design around that.
Pair it with a plain text address field. The field is a location picker, not an address parser. If you need a canonical postal address, keep a separate address string column and treat the map payload as the geo anchor.
Distance / proximity queries. Because lat/lng sit in JSON, either add generated or duplicated lat/lng columns for indexing, or use a haversine expression:
$places = Place::selectRaw('*, ( 6371 * acos( cos( radians(?) ) * cos( radians( lat ) )
* cos( radians( lng ) - radians(?) ) + sin( radians(?) ) * sin( radians( lat ) ) ) ) AS distance',
[$lat, $lng, $lat])->having('distance', '<', 25)->get();
For anything beyond toy volumes, extract real columns.
Validation rule. Nova will happily save an empty pin. Add a custom rule that asserts both lat and lng exist and are within range (-90..90, -180..180).
Consuming on the frontend. Pass the coordinates to a Blade view and render a static map or an embedded map using an HTTP-referrer-restricted key (different from the admin key).
Multi-point / reordering. For "many locations" (e.g. a repeater), lean on Nova's built-in repeatable patterns or a sibling package; this field represents a single point.
RefererNotAllowedMapError, it's the restriction, not the package.{} vs null. Saved-then-cleared pins may arrive as {"lat":null,"lng":null}. Guard every accessor with ?? null and is_numeric checks before doing math.decimal(10,7) columns; keep the JSON only if the payload also carries a label.npm run dev/prod inside the package directory or copy the component into your app), and rebuild — otherwise your edits silently wash out on composer update.config:cache after changing keys → verify the model cast is actually array and not string.->zoom(), default-coordinates, and hide-search style helpers (where present) are thin wrappers around component props — inspect the published Vue source to see exactly which props the field passes before assuming a method exists.How can I help you explore Laravel packages today?