Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Google Map Laravel Package

ghanem/google-map

A Laravel package that makes it easy to embed Google Maps in your app. It provides Blade components and a fluent PHP API for rendering maps, markers, info windows, and shapes, with configurable options for building interactive, location-aware interfaces without heavy JavaScript boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire it up

composer require ghanem/google-map
php artisan vendor:publish --provider="Ghanem\GoogleMap\FieldServiceProvider"   # config + assets, tag names in the README

Requires Laravel Nova 4 or 5 (nova >= 4.0); check composer.json of the release you pull — Nova 5 support landed late, so pin if you're on an older Nova.

Get a browser key, not a server key. In Google Cloud Console enable Maps JavaScript API and Places API (the autocomplete widget will silently do nothing if only Maps JS is enabled). Create an API key, restrict it by HTTP referrer to your Nova host(s), and put it in .env:

GOOGLE_MAPS_API_KEY=AIza...

The package reads the key from the published config (config/google-map.php) or config('services.google.maps_api_key') depending on version — open the published config file first and follow whatever key it actually reads. Then php artisan config:cache.

Migration and first field. The field maps to columns you define; the common shape is three columns:

Schema::table('stores', function (Blueprint $table) {
    $table->decimal('latitude', 10, 7)->nullable();
    $table->decimal('longitude', 10, 7)->nullable();
    $table->string('address')->nullable();
});
use Ghanem\GoogleMap\GoogleMap;

public function fields(NovaRequest $request): array
{
    return [
        GoogleMap::make('Location')
            ->latitude('latitude')
            ->longitude('longitude')
            ->address('address')
            ->required(),
    ];
}

Start with one field on one Resource form — a "store address" or "venue location" — and confirm both the marker and the address input round-trip on save. That single use case is 90% of what this field is for.

Implementation Patterns

Separate columns vs. a single JSON column. Two viable shapes:

  • Disaggregated (above): easiest to query, sort and validate; best if you'll ever filter by bounding box or distance.
  • Aggregated: $table->json('location') plus protected $casts = ['location' => 'asArrayObject'], and configure the field to hydrate the whole attribute. Less schema churn, but every read needs a cast.

If the field hydrates separate attributes, be aware it fills them from the field's payload on save; if you need derived values (city, country, formatted address from Places address_components), do it in fillUsing() or a model saving observer rather than trusting the browser payload.

Displaying it outside the form. Don't leave the field on index — the map component is heavy and useless in a table cell. Use:

GoogleMap::make('Location')->onlyOnForms(),
Text::make('Address')->onlyOnIndex()->sortable(),
Text::make('Coordinates', fn ($m) => "{$m->latitude}, {$m->longitude}")->onlyOnDetail(),

Or link out: ->displayUsing(fn ($v, $m) => ...) returning a https://www.google.com/maps/search/?api=1&query=lat,lng anchor (wrap in new HtmlString via a custom Text subclass, since Nova escapes by default).

Distance queries. Once lat/lng live in columns you get the real payoff: a Haversine scope for "stores near me", then feed the result into an index table or a Distance filter. Keep the math in SQL (ST_Distance_Sphere on MySQL 8 / PostGIS ST_DWithin), not in PHP.

Geocoding on the server. For imports, seeders and API-created records there is no browser: geocode in a queued job using googlemaps/google-maps-services-php or a plain HTTP call, write lat/lng, and let the Nova field open in read-only style for those records. Never geocode synchronously in a saving hook — a flaky Google call becomes a failed form submit and a billable request per keystroke of retry.

Programmatic prefills and defaults. For "copy billing address to store address" style flows, set defaults via ->default() where supported, or push values through ->withMeta() from the Resource so the component centres correctly. If the map keeps resetting on dependsOn() re-evaluation, that's the reactive re-render, not your data.

Read-only / audit views. On detail pages, prefer the field with a locked/disabled variant or skip it entirely and render a static image via the Static Maps API — cheaper and no JS on a page users only glance at.

Gotchas and Tips

  • The key is in the page source. It has to be. Referrer restrictions plus a separate server-side key for geocoding are non-negotiable; an unrestricted browser key is a billing incident waiting to happen.
  • A grey box means one of three things: billing not enabled on the project, Maps JS not enabled for that key, or a referrer/CSP block. Check the browser console — Google returns an explicit auth error you can read.
  • Places API (New) vs legacy. Autocomplete silently fails if you enabled only the new Places API but the widget uses the legacy places library (or vice versa). Enable both during bring-up, then trim.
  • config:cache traps. An env var added after a cached config will not appear; re-run php artisan config:cache on every deploy. If the key is passed through the field's metadata rather than config(), a VIEW_COMPILED cache won't help you — clear Nova's asset cache with php artisan nova:publish after upgrades.
  • Precision. Use decimal(10,7)/decimal(11,8) and cast to float on read. Strings compare badly and JSON floats round-trip unevenly; (0, 0) means "no marker", which drops you in the Gulf of Guinea — always set a default map centre.
  • Modal and Flexible/panel layouts. Autocomplete dropdowns render inside a stacking context; inside Modal or Nova 5 panels the suggestions list can clip or sit behind the footer. Either keep the field top-level on the form or bump the container's overflow/z-index in a published Vue override.
  • Client-side values are user-editable. Re-validate latitude/longitude range and address length with ->rules() and again in a FormRequest; the marker position is just JSON in the request.
  • Multiple map fields on one form each instantiate a map and each autocomplete session is billed. Collapse to one location field per resource unless the domain genuinely needs several.
  • Dark mode. Check the Nova 5 dark theme — Google's default map style is bright; either accept it or ship a custom styles array through the field options.
  • Testing. The component is JS; feature tests only see the submitted payload. Write tests that post lat/lng/address directly, and cover the server-side geocode job separately. Don't expect assertSee to prove the map rendered.
  • Extension points: subclass the field and override component() to point at your own published Vue component when you need custom behaviour (radius overlay, marker dragging, "use my location" button). The published assets and config are the intended seams; there are no other public APIs to lean on.
  • Bus factor. Eight stars and a single maintainer — vendor-lock your version in composer.json and keep the published Vue component in your repo so a Nova 6 breaking change doesn't strand you. Budget half a day to fork-and-maintain if that day ever comes.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource