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

petrepatrasc/google-map

A PHP package for integrating Google Maps into your application, providing a simple wrapper for embedding maps, working with locations, and connecting to Google Maps services. Suitable for Laravel or standalone PHP projects that need map rendering and geodata support without boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

Installation

composer require petrepatrasc/google-map

There is no Laravel service provider, no facade, no auto-discovery — this is a plain PHP library from the PHP 5.3 era that you wire up yourself.

Where to look first

  • src/Ivory/GoogleMap/Map.php — the central object; every overlay, control, and event hangs off it.
  • src/Ivory/GoogleMap/Helper/MapHelper.php — turns a Map into the HTML container + the google.maps.Map JavaScript. This is what you'll actually echo in your Blade views.
  • src/Ivory/GoogleMap/Overlays/ — Marker, InfoWindow, Polyline, Polygon, Circle, Rectangle, GroundOverlay, EncodedPolyline.
  • src/Ivory/GoogleMap/Base/Coordinate.php — lat/lng value object used everywhere.
  • src/Ivory/GoogleMap/Services/ — Geocoding\Geocoder, Directions\Directions, DistanceMatrix\DistanceMatrix, Places\Places.

First use case — drop a marker on a map in a Blade view

In a controller (or a small dedicated MapBuilder class):

use Ivory\GoogleMap\Map;
use Ivory\GoogleMap\Base\Coordinate;
use Ivory\GoogleMap\Overlays\Marker;
use Ivory\GoogleMap\Helper\MapHelper;

$map = new Map();
$map->setHtmlContainerId('map_canvas');
$map->setCenter(new Coordinate(51.5, -0.12));
$map->setMapOption('zoom', 12);

$map->addMarker(new Marker(new Coordinate(51.5, -0.12)));

$helper = new MapHelper();

return view('map', [
    'mapHtml' => $helper->renderHtml($map),
    'mapJs'   => $helper->renderJs($map),
]);

In resources/views/map.blade.php:

{!! $mapHtml !!}
<script src="https://maps.googleapis.com/maps/api/js?key={{ config('services.google.maps_key') }}"></script>
{!! $mapJs !!}

That's the entire happy path. If that renders a map, you're 90% of the way to using the package productively.

Implementation Patterns

Bind the helper as a singleton

MapHelper is stateless-ish and cheap to reuse. Register it once so you can inject it:

// app/Providers/AppServiceProvider.php
$this->app->singleton(\Ivory\GoogleMap\Helper\MapHelper::class);

Encapsulate map construction

Don't build maps inside controllers. A MapFactory / MapPresenter that accepts a DTO or Eloquent collection keeps things testable:

class StoreMapBuilder
{
    public function __construct(private MapHelper $helper) {}

    public function build(Collection $stores): array
    {
        $map = new Map();
        $map->setHtmlContainerId('stores_map');
        $map->setAutoZoom(true);          // let the SDK fit markers
        $map->setCenter(new Coordinate(0, 0));

        foreach ($stores as $store) {
            $marker = new Marker(new Coordinate($store->lat, $store->lng));
            $marker->setIcon('/img/pin.png');
            $marker->setInfoWindow($this->infoWindow($store));
            $map->addMarker($marker);
        }

        return [
            'html' => $this->helper->renderHtml($map),
            'js'   => $this->helper->renderJs($map),
        ];
    }
}

Two-part rendering is the core workflow

renderHtml() produces the container <div> (and any required CSS class setup); renderJs() produces the <script> block building the map. Output them in the right order and after the Google Maps SDK script tag. If you use layouts, put renderJs() in a @stack('scripts') push so it lands before </body>.

Events

Use the package's event objects rather than hand-written JS blobs:

use Ivory\GoogleMap\Events\MouseEvent;
use Ivory\GoogleMap\Events\Event;

$marker->setInfoWindow($infoWindow);
$marker->addEvent(
    new Event('click', 'function(){ ... }')
);

MouseEvent::CLICK, MOUSEOVER, MOUSEOUT constants exist for the common cases; anything else takes a raw event name string.

Services (geocode / directions / places)

These are thin HTTP clients around the Google Web Service APIs. They accept PSR-ish request objects and return typed response objects, so you can consume them inside a queued job or command without the JS SDK:

$geocoder = new Geocoder();
$response = $geocoder->geocode('10 Downing Street, London');

foreach ($response->getResults() as $result) {
    $result->getGeometry()->getLocation();   // Coordinate
}

Cache these calls aggressively — Google bills per request and this library does no caching.

Blade component / directive

Wrap the two render calls in a component so views stay clean:

<x-google-map :html="$mapHtml" :js="$mapJs" :api-key="config('services.google.maps_key')" />

Extension points

  • Extend AbstractOverlay (or Marker directly) for custom overlays.
  • Extend AbstractControl for custom control positioning.
  • Extend AbstractService if you need to point at a different base URL (e.g., a proxy) or add request signing.
  • Map::setMapOption($key, $value) is a passthrough to the JS map options — the fastest escape hatch when the library lacks a typed setter.

Gotchas and Tips

PHP 8 compatibility is the biggest risk. This is a PHP 5.3-era codebase with zero dependents and zero stars. Expect no typed properties, no return types, and possible curly-brace string offsets, dynamic property deprecations, or @-silenced calls that break on PHP 8.1+. Before committing, run a smoke test on your actual PHP version. If it fails, you're maintaining a fork, which changes the calculus — this is precisely when evaluating a maintained alternative (e.g. a modern client that only emits JS config, or calling the Maps JS API directly from a Blade component) is worth the 30 minutes.

No service provider = no config publishing. You own API key loading, container binding, and facade creation. Don't assume config('google-map.*') exists.

Script tag ordering is fragile. renderJs() emits code that assumes google.maps is already defined. If you load the SDK with async/defer or through a bundler, the map won't initialize. Either load the SDK synchronously before renderJs() output, or wrap your output in the SDK's callback= parameter.

setAsync(false) vs. setAsync(true). The async map option changes how the helper's JS behaves. Default to synchronous unless you have a specific reason; async + renderJs() in the wrong place silently produces a blank container.

Container ID collisions. setHtmlContainerId() must be unique per page. If you render multiple maps in a loop, append a suffix; the helper does not namespace anything for you.

Auto-zoom overrides your center. setAutoZoom(true) with no overlays can produce a degenerate viewport. Add at least one marker or set setBound() explicitly if you're relying on it.

Debug by reading generated JS, not the PHP. When a map fails to appear, dump renderJs() output to the console and eyeball the new google.maps.Map(...) call. The library is a string-builder at heart; bugs almost always show up as malformed JS (unescaped quotes in info-window content, missing commas in option arrays). Info window HTML especially needs escaping — pass it through e() in Blade or escape before handing it to the library.

Escape user content in info windows. InfoWindow::setContent() accepts raw HTML and the helper interpolates it into a JS string literal. Store names, addresses, or anything user-controlled must be escaped; this library will not do it for you, and it's a real XSS footgun.

API key placement. Older code samples in the README assume the key is appended to the maps.googleapis.com/maps/api/js URL. Modern Google Maps requires ?key=.... The library doesn't manage this — always emit the SDK tag yourself with your key from config/services.php.

No caching in the service classes. Geocoder, Directions, and DistanceMatrix will happily fire a request every call. Wrap them in Cache::remember() keyed on the normalized address/route, and put them behind queued jobs if volume is non-trivial.

Abandonment signal. Zero stars, zero dependents, no tagged releases — treat this as read-only vendored code. If you adopt it, pin the exact commit in composer.json, and plan for the possibility that upgrading Laravel/PHP will force you to either fork it or replace it. A thin internal wrapper (MapBuilder + one Blade component) contains that blast radius to a single class, which is the pragmatic mitigation if you do use it.

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