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 Form Type Bundle Laravel Package

oh/google-map-form-type-bundle

A Symfony bundle that adds a Google Maps form type for selecting locations directly in your forms. Let users pick a point on an interactive map and store latitude/longitude with minimal configuration. Ideal for address, venue, or geolocation fields.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the framing: despite the "Laravel developer" lens, oh/google-map-form-type-bundle is a Symfony bundle, not a Laravel package. There is no service provider, no config publish tag, nothing Composer-autoloadable into a Laravel form builder. If you are on Laravel, treat it as a reference implementation (read the JS + Twig to see how a draggable-marker → lat/lng pair is wired) and rebuild it as a Blade component + small Alpine/Stimulus controller. Everything below assumes Symfony.

Install

composer require oh/google-map-form-type-bundle

Expect the constraint solver to fight you — the last release is 2016-07-21, so the symfony/form / symfony/framework-bundle constraints target Symfony 2.x–3.x. On any modern project you will be forking the repo or using --ignore-platform-reqs purely to read the code.

Register it

  • Symfony 4+: config/bundles.php → Oh\GoogleMapFormTypeBundle\OhGoogleMapFormTypeBundle::class => ['all' => true]
  • Symfony 2/3: app/AppKernel.php → new Oh\GoogleMapFormTypeBundle\OhGoogleMapFormTypeBundle()

Configure the API key (Google has required a key + billing account on the Maps JS API since 2016, which this bundle predates):

# config/packages/oh_google_map_form_type.yaml
oh_google_map_form_type:
    api_key: '%env(GOOGLE_MAPS_API_KEY)%'

Look here first, not at the README — the docs are thin and the code is small:

  1. Form/Type/GoogleMapType.php — the compound type; this defines lat, lng, and every option you can pass.
  2. Resources/views/Form/*.twig — the widget template; confirms required asset names, DOM ids, and data-* attributes the JS reads.
  3. Resources/public/js/*.js — the initialiser; check the Maps API version and whether the <script> URL includes &key=.

Publish/install assets

php bin/console assets:install --symlink public/

Or, if you use Webpack Encore, copy the bundle JS into assets/ and import it yourself — cleaner and avoids assets:install in deploy pipelines.

First use case — a venue/address form where the user drops a pin:

use Oh\GoogleMapFormTypeBundle\Form\Type\GoogleMapType;

$builder
    ->add('name')
    ->add('latlng', GoogleMapType::class, [
        'lat_options' => ['label' => 'Latitude'],
        'lng_options' => ['label' => 'Longitude'],
        'map_options' => ['width' => '100%', 'height' => '320px', 'zoom' => 12],
    ]);
{% form_theme form 'OhGoogleMapFormTypeBundle:Form:google_map_widget.html.twig' %}
{{ form_start(form) }}
    {{ form_row(form.name) }}
    {{ form_row(form.latlng) }}
{{ form_end(form) }}

If you see two plain text inputs and no map, the form theme or the JS asset is missing — see Gotchas.

Implementation Patterns

Wrap it in a domain type. Don't scatter GoogleMapType across every form. Make a LocationType that composes it, so the map options stay in one place:

class LocationType extends AbstractType
{
    public function buildForm(FormBuilderInterface $b, array $o): void
    {
        $b->add('latlng', GoogleMapType::class, [
            'label' => false,
            'map_options' => ['height' => '320px', 'zoom' => 14],
        ]);
    }
}

Map to your entity. The type is compound, so your entity needs two writable properties (latitude, longitude). If you model the point as a single column (PostGIS/MySQL POINT, or a "lat,lng" string), either:

  • use 'property_path' => false on the child fields plus a PRE_SUBMIT listener that parses the pair into your value object, or
  • add a DataTransformer on the parent so location ⇄ [lat, lng].

Store coordinates as DECIMAL(10,7) / DECIMAL(11,8), not FLOAT. Float rounding on lng at 7 decimals is ~1cm, which is fine for pins but bites you the moment you do distance filters or equality checks. Also decide up front what null means (no pin dropped) versus 0,0 (a real place in the Gulf of Guinea).

Validate server-side, always. The map is a UI convenience; the POST body is attacker-controlled.

->add('latitude', NumberType::class, [
    'constraints' => [new Assert\Range(['min' => -90, 'max' => 90])],
])

Default centre / zoom for empty state. When the entity has no coordinates yet, centre on a configured fallback (city HQ, country centroid) rather than letting Google default to the whole world. Pass it through map_options (the type typically accepts a lat/lng/zoom seed) — verify the exact option names against GoogleMapType::configureOptions, since they changed between versions.

Don't expect geocoding. This bundle places a marker and reads coordinates back out. It does not do address → lat/lng in either direction. For a real address workflow you need:

  • Forward geocoding: a server-side call (Google Geocoding API, Mapbox, Nominatim) on submit; never trust a client-side geocoder for stored data.
  • Places Autocomplete: a separate input and a small JS listener that calls map.setCenter() on your map instance. You'll have to expose the map object from the bundle's initialiser or re-initialise yourself.
  • Reverse geocoding: hook a drag event → fetch address → fill the address field. This is where people usually end up replacing the bundle's JS wholesale.

Progressive enhancement. The lat/lng inputs exist as real form fields. Keep them visible (or at least present) so the form is submittable and correct without JS — that also makes it testable with $client->submitForm() in WebTestCase, where the map never renders.

Admin panels. For EasyAdmin/Sonata, this is a custom field/configurator job rather than a drop-in add('latlng', GoogleMapType::class). The cost of wiring is usually higher than a hand-rolled field with three lines of JS — worth measuring before you commit.

Multiple pins (e.g. a delivery route) is out of scope for this bundle. One map, one marker. For N points, write your own widget; the bundle's marker-centering logic doesn't generalise.

Gotchas and Tips

A grey/blank map is almost always the API key. The bundle's JS emits a <script src="https://maps.googleapis.com/maps/api/js"> built before keys were mandatory. Check the rendered HTML: if there's no &key=..., you must override the script tag yourself in your layout or patch the template. A key without a billing account enabled also fails, silently-ish, with a console error.

Hardened key restrictions. Set HTTP-referrer restrictions on the key and do not use it server-side. The key is in your page source by definition — restrict it and monitor it.

The Twig form theme is not auto-registered. form_theme must appear in the template that renders the form (or in twig.yaml globally). Miss it and you get two unstyled <input type="text">, which looks like "the bundle is broken".

Field name collisions with the JS. The initialiser typically locates map containers and inputs by generated ids derived from the form field name (form_latlng_lat). Render two of these forms on one page and the ids collide, or the JS picks the wrong one. Either namespace your form names (createNamed) or initialise per-container in your own script.

Asset serving in production. assets:install output isn't committed in most setups; if you skip it in the deploy script the map 404s while everything else works. With Encore, import the file so it gets fingerprinted — but note the bundle JS may assume it runs after the Maps API callback, so order imports deliberately.

HTTPS-only. Mixed-content blocking of the Maps API script produces a console error and a blank canvas that looks identical to a bad key.

Debugging checklist, in order: (1) view source — is the API script tag present and does it contain key=; (2) is the widget markup present (data-*/container div), meaning the form theme applied; (3) browser console for ReferenceError: google is not defined (script order) vs InvalidKeyMapError (key) vs RefererNotAllowedMapError (restrictions); (4) Network tab for a 404 on the bundle's JS.

Extension points, most sustainable first:

  • Replace the JS. Keep the form type (it's just two number fields + options), delete the bundled script, and write ~40 lines of vanilla JS or an Alpine component. Given the 2016 provenance and the Maps API's evolution, this is what most teams end up doing within a day.
  • Override the template at templates/bundles/OhGoogleMapFormTypeBundle/Form/... to control markup and add your own data- hooks.
  • Subclass GoogleMapType to add options or change defaults; register it as the GoogleMapType::class alias if you want every call site upgraded at once.
  • Fork. With 15 stars and zero dependents, upstream fixes aren't coming. A fork pinned in repositories with vcs type is a legitimate, low-risk move for a package this small.

Plan the exit. This is a UI convenience of a few hundred lines. Budget a half-day to inline it rather than carrying an unmaintained Symfony 2-era dependency through your next major framework upgrade — you'll spend less time than on one Composer constraint fight.

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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect