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

Googlmapper Laravel Package

cornford/googlmapper

GooglMapper makes it easy to add Google Maps to Laravel apps. Create maps, markers, info windows, and overlays with a simple PHP API, customize controls and styles, and render interactive maps in Blade views without writing JavaScript.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish

composer require cornford/googlmapper
php artisan vendor:publish --provider="Cornford\Googlmapper\MapperServiceProvider"

Laravel 5.5+ discovers the service provider and Mapper facade via the package's composer.json extra.laravel block. On older versions you register Cornford\Googlmapper\MapperServiceProvider::class and alias Mapper to Cornford\Googlmapper\Facades\MapperFacade::class in config/app.php.

Open config/mapper.php first. The only key that must be set before anything works is key — your Google Maps JavaScript API key. The rest (region, language, defaults.map, defaults.marker, cache) are defaults you can override per-map. This file is the single most useful thing to read when a map misbehaves.

First use case — one map, one marker

use Cornford\Googlmapper\Facades\MapperFacade as Mapper;

Route::get('/contact', function () {
    Mapper::map(53.381129, -1.470085, [
        'zoom'    => 15,
        'type'    => 'ROADMAP',
        'height'  => '420px',
        'markers' => ['title' => 'Sheffield HQ', 'animation' => 'DROP'],
    ]);

    return view('contact');
});
{{-- resources/views/contact.blade.php --}}
{!! Mapper::render() !!}

Mapper::render() emits three things in one call: the <script src="https://maps.googleapis.com/maps/api/js?..."> tag (once per request), a sized <div id="map_1">, and the inline new google.maps.Map(...) initializer. @map is registered by the service provider as shorthand if you prefer Blade directives.

First diagnostic step when nothing shows: view-source on the page. You should see the maps.googleapis.com script tag, a map_1 div with a non-zero height, and an initializer block. Missing script tag → config/registration problem. Missing div → you didn't call render(). Zero-height div → CSS problem, not a Maps API problem.

Implementation Patterns

Chaining shapes onto a single map

Every builder method returns the mapper instance, so chaining is the natural style. Shapes attach to the most recently declared map:

Mapper::map(53.38, -1.47, ['zoom' => 14])
    ->marker(53.381, -1.470, ['title' => 'HQ', 'animation' => 'DROP'])
    ->informationWindow(53.381, -1.470, '<h4>HQ</h4><p>Main office</p>', ['markers' => false])
    ->circle(53.381, -1.470, 500, ['strokeColor' => '#1E90FF', 'fillOpacity' => 0.1])
    ->polyline([[53.38, -1.47], [53.40, -1.45]], ['strokeColor' => '#e50000']);

Multiple maps per page

Each map() call increments an internal id (1, 2, 3…). Mapper::render() outputs all of them; Mapper::render(2) renders a single one by id. The common pattern is an "overview" map plus a "directions" map side by side. Remember each map instantiates its own google.maps.Map on page load — watch your tile quota on data-heavy pages.

Populating from Eloquent

Mapper::map($store->lat, $store->lng, ['zoom' => 10, 'cluster' => true, 'height' => '500px']);

foreach ($stores as $store) {
    Mapper::marker($store->lat, $store->lng, [
        'title' => $store->name,
        'icon'  => $store->isOpen() ? '/img/pin-green.png' : '/img/pin-grey.png',
    ]);
}

Set cluster => true on the map once you exceed a handful of markers — the package pulls in the markerclusterer library for you.

Async loading for off-screen maps

Pass 'async' => true in the map options. The script tag gains a callback and the initializer waits for the API to resolve. Useful when the map is far below the fold and you want it out of the critical path. Doesn't help if you're rendering inline in a partial that also owns the div — the div still has to exist in the DOM before the API callback fires.

Caching

// config/mapper.php
'cache' => [
    'enabled'  => true,
    'key'      => 'googlmapper',
    'lifetime' => 60, // minutes
],

Only enable this for maps whose markers rarely change (contact pages, static store locators). The cache key does not automatically reflect database changes, so user-generated marker sets will go stale.

Testing

Facade mocking is the sane approach — never exercise the real renderer in a feature test:

Mapper::shouldReceive('map')->once()->with(53.381, -1.470, Mockery::type('array'))->andReturnSelf();
Mapper::shouldReceive('marker')->andReturnSelf();
Mapper::shouldReceive('render')->andReturn('<div id="map_1"></div>');

Assert the controller calls map() with the right coordinate arguments; leave JavaScript rendering to a browser test if you need one.

Gotchas and Tips

The API key is mandatory and errors aren't surfaced. Google removed keyless Maps access in 2018. A grey "For development purposes only" watermark means the key is missing from config/mapper.php, restricted to the wrong HTTP referrer, or the Maps JavaScript API isn't enabled in the Cloud project. The package does not bubble these errors — check the browser console.

The package is effectively unmaintained. Last release October 2019. It predates AdvancedMarkerElement (the replacement for google.maps.Marker, which Google is progressively deprecating), Map IDs for cloud-based styling, and modern Map constructor patterns. It still works against the current JS API, but you are on a legacy path. Wrap your usage inside a thin app-level service (MapService::renderForLocations($collection)) so you can swap implementations later without touching every controller.

Zero-height divs. The package will output height:400px only if you pass the height option to map(). Without it, you get a div that collapses. Either set 'height' => '500px' on every map() call, or target #map_1 in your stylesheet.

Hidden containers break rendering. Maps inside Bootstrap tabs, modals, or anything with display:none at load time render at zero size. The standard fix applies: after showing the panel, google.maps.event.trigger(map, 'resize'). Or move the map container outside the hidden region.

Options are passed straight through to the Google Maps constructor — unvalidated. A typo like 'animiaton' => 'DROP' silently does nothing. Keep the JS API reference open whenever you're writing a new option payload; nothing will warn you.

InfoWindows accept raw HTML. Mapper::informationWindow($lat, $lng, $content) renders $content unescaped into the map. If it comes from user input, run it through e() or htmlspecialchars() upstream.

Cluster + custom icons is fiddly. Custom icon URLs work fine on markers, but custom clusterRenderer styling isn't exposed by the builder API. You'd need to post-process the map object after initialization with your own JavaScript.

Geocoding is server-side and billed separately. Passing an address string instead of a coordinate triggers a Geocoding API request from PHP for each map. That's an extra API call (and an extra failure point) on every page render. If you already have lat/lng in the database, always use them.

Config precedence. Per-map options override config('mapper.defaults.map'). When a single page behaves differently from its siblings, hunt for the override in the controller before touching the config file.

Lumen. No auto-discovery. Register the provider manually in bootstrap/app.php, alias the facade, and copy config/mapper.php into your config/ directory.

Extension points are thin. All rendered markup and JavaScript comes from Blade views shipped inside the package (vendor/cornford/googlmapper/resources/views). Publish and override those if you need different wrapper markup. There is no event or hook surface for the JS itself — for per-map custom JavaScript, @push('scripts') in your layout and reach the instance by its map_1-style id.

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.
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo
bernskioldmedia/laravel-harvest
bernskiold/laravel-data-scrubber
bernskiold/laravel-currency-converter
bernskiold/laravel-campaign-monitor
bernskioldmedia/laravel-autotranslate
siberfx/telegram-bot
roy-sandip/laravel-view-extended
gregsphotography/site-builder-core
roy-sandip/laravel-view
anodyne/blade-fluentui-icons
webx-ui/module-catalog-collections