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

radutopala/google-map

Laravel package for integrating Google Maps into your application, with configuration and Blade helpers for rendering maps and markers. Lightweight and easy to wire into existing views. Note: it has no stars and a low health score, so check maintenance status before using in production.

View on GitHub
Deep Wiki
Context7

Getting Started

radutopala/google-map is a framework-agnostic PHP library (a fork of egeloen/ivory-google-map) that builds the HTML + JavaScript needed to render a Google Maps JS API v3 map. There is no Laravel service provider, no facade, and no config file — you get PHP objects that serialize to JS, and you wire them into Blade yourself.

Install and confirm the tag supports your PHP version (the package description still says PHP 5.3+, which is the original library's floor; the fork's composer.json is the source of truth):

composer require radutopala/google-map

Where to look first, in this order:

  1. Ivory\GoogleMap\Map — the root object (container id, center, zoom, overlays, events, controls).
  2. Ivory\GoogleMap\Helper\Builder\MapHelperBuilder / ApiHelperBuilder — the renderers.
  3. Ivory\GoogleMap\Overlay\* — Marker, InfoWindow, Polyline, Polygon, Circle, EncodedPolyline.
  4. Ivory\GoogleMap\Service\* — server-side REST wrappers (Geocoder, Directions, DistanceMatrix, Elevation, Place, TimeZone).

First use case — a map with one marker, rendered in a controller:

use Ivory\GoogleMap\Map;
use Ivory\GoogleMap\Base\Coordinate;
use Ivory\GoogleMap\Overlay\Marker;
use Ivory\GoogleMap\Overlay\InfoWindow;
use Ivory\GoogleMap\Helper\Builder\MapHelperBuilder;
use Ivory\GoogleMap\Helper\Builder\ApiHelperBuilder;

$map = new Map('office-map');                 // first arg = DOM element id
$map->setCenter(new Coordinate(48.8566, 2.3522));
$map->setZoom(12);

$marker = new Marker(new Coordinate(48.8566, 2.3522));
$marker->setInfoWindow(
    (new InfoWindow())->setContent('<strong>HQ</strong>')
);

$map->getOverlayManager()->addMarker($marker);

$mapHelper = MapHelperBuilder::create()->build();
$apiHelper = ApiHelperBuilder::create()
    ->setKey(config('services.google_maps.key'))
    ->build();

return view('map', [
    'api' => $apiHelper->render([$mapHelper]),   // <script src=...> + init callback
    'map' => $mapHelper->render($map),           // <div> + inline <script>
]);
{{-- resources/views/map.blade.php --}}
{!! $api !!}
{!! $map !!}

That is the whole loop: build a Map, hand it to a helper, echo the strings. If you internalise nothing else, internalise renderHtml() / renderJs() (separate pieces, useful when you want the <div> in Blade and the JS at the bottom of the body).

Implementation Patterns

Wrap construction in a service, not a controller. Maps get built from Eloquent collections constantly; give yourself one place that knows how to translate models into overlays.

// app/Services/MapFactory.php
class MapFactory
{
    public function forLocations(Collection $locations, string $id = 'map'): Map
    {
        $map = new Map($id);
        $map->setCenter(new Coordinate(
            $locations->avg('lat') ?: 48.8566,
            $locations->avg('lng') ?: 2.3522,
        ));
        $map->setAutoZoom(true); // fits bounds to overlays, overrides zoom

        foreach ($locations as $loc) {
            $marker = new Marker(new Coordinate($loc->lat, $loc->lng));
            $marker->setInfoWindow(
                (new InfoWindow())->setContent(view('partials.map-bubble', compact('loc'))->render())
            );
            $map->getOverlayManager()->addMarker($marker);
        }

        return $map;
    }
}

Bind the helper once. MapHelperBuilder::create()->build() is cheap but not free, and you'll want consistent options (formatter, JSON builder) everywhere — register it as a singleton in AppServiceProvider.

Blade component for the two-part output. A <x-google-map :map="$map" :id="'office-map'" /> component that emits {!! $apiHelper->render([$mapHelper]) !!}{!! $mapHelper->render($map) !!} keeps views clean and gives you one place to handle the "JS API already loaded" problem (see Gotchas).

Server-side geocoding, cached. The Service namespace saves you from writing Guzzle calls, but hit it through Cache::remember keyed on the normalised address — Google bills per request and rate-limits aggressively:

use Ivory\GoogleMap\Service\Geocoder\GeocoderService;

$service = new GeocoderService($httpAdapter, config('services.google_maps.key'));

$coordinate = Cache::remember("geo:{$hash}", now()->addMonth(), function () use ($service, $address) {
    $result = $service->geocode($address)->getResults()[0] ?? null;
    return $result ? $result->getGeometry()->getLocation() : null; // Coordinate
});

Directions / DistanceMatrix are the same shape: build a request object, call one method, walk a response object. Great for delivery-radius or "distance to nearest branch" features.

Dynamic overlays via event listeners. Events are raw JS strings injected verbatim — that's your extension point for anything the library doesn't model (drawing tools, click-to-add-marker, custom clustering):

$map->getEventManager()->addEvent('click', sprintf(
    '%s.addMarker(new google.maps.Marker({position: e.latLng, map: %s}));',
    $map->getVariable(),
    $map->getVariable()
));

Use $map->getVariable() (never a hardcoded map) so multiple maps on one page don't collide.

AJAX / Livewire partial reloads. render() produces a <div> + an IIFE that runs immediately. On SPA-style updates, either render only renderJs() into a dispatched browser event, or make the component re-mount the container element so the inline script re-executes.

Testing. Output is a deterministic string — assert on it rather than on a browser:

$js = MapHelperBuilder::create()->build()->renderJs($map);
$this->assertStringContainsString('new google.maps.Marker', $js);
$this->assertStringContainsString('"lat":48.8566', $js);

Gotchas and Tips

  • It is a JS-API wrapper, not a tile server. Nothing renders without an API key, and since Google's 2018 pricing change, a key without billing enabled gives you the greyed-out "For development purposes only" watermark. Set services.google_maps.key from GOOGLE_MAPS_KEY and never commit it.
  • Load the JS API exactly once. $apiHelper->render([$mapHelper1, $mapHelper2, ...]) accepts an array for a reason — pass every helper and it emits one <script> tag plus a callback that initialises them all. Two separate render() calls = two <script src> tags = "You have included the Google Maps JavaScript API multiple times" and possibly a blank map.
  • Container IDs must be unique. The Map constructor's first argument is the DOM id and seeds the JS variable name. Building the same map object twice, or reusing 'map' in two components, produces duplicate id attributes and only one map works.
  • Latitude first. new Coordinate($lat, $lng) everywhere. Easy to get backwards when your DB columns are longitude/latitude; write one accessor and use it.
  • setAutoZoom(true) silently beats setZoom(). If your zoom is being ignored, that's why. Same for setCenter() — if you provide overlays, the center becomes a fallback only.
  • Event handlers and InfoWindow::setContent() are injected raw into a JS string. Never pass user input directly. Run it through json_encode($html) or render a Blade partial and json_encode the result — otherwise a quote in a product name breaks the whole map script.
  • PHP 8 compatibility is your problem, not the package's. This fork has essentially no community (0 dependents). Pin a tag, run composer why-not php 8.3, and expect to patch return types or array-offset warnings yourself. For a new project, weigh googlemaps/google-maps-services-php (server-side only) or just calling the JS API directly from Alpine/Vue.
  • CSP will block it. You need script-src https://maps.googleapis.com 'unsafe-inline' (the library emits inline scripts) and img-src https://maps.gstatic.com data:.
  • No clustering, no drawing tools, no marker spidering. The library models the v3 API surface as of ~2016. For MarkerClusterer, heatmaps with weight, or advanced markers you'll drop to raw JS via getEventManager() or emit your own <script> alongside the helper output.
  • Services need an HTTP client you must supply. Depending on the tag it's an Ivory\HttpAdapter\HttpAdapterInterface or a PSR-18 client — check composer.json before assuming Guzzle. Inject it; don't new it inside a controller.
  • Debugging tip: when a map is blank, dump $mapHelper->renderJs($map) into a <pre> and paste it into the browser console. Errors like InvalidValueError: setCenter: not a LatLng are almost always a missing setCenter(), a null coordinate from a geocode that returned zero results, or a coordinate built from strings.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle