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

Geographical Bundle Laravel Package

vich/geographical-bundle

A Symfony bundle for working with geographical data: countries, states, zones, and multilingual names. It provides form types, a Doctrine-backed geographical hierarchy, and locale-aware selectors, making it easy to add country and region fields to your entities and forms.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: vich/geographical-bundle is a Symfony2 (2.0–2.8 era) bundle, not a Laravel package. There is no service provider, no facade, no publishable config. For a Laravel team the realistic outcome is one of two things: (a) you are auditing a legacy Symfony2 app that already uses it, or (b) you are mining it for a proven design pattern to reimplement with Eloquent + Blade. Do not attempt composer require vich/geographical-bundle in a Laravel 9/10/11 app — the constraints (symfony/framework-bundle ^2.x, Doctrine 2.2-era, PHP 5.3+) will not resolve.

If you are in the Symfony2 app it was built for:

composer require vich/geographical-bundle:dev-master
// app/AppKernel.php
new Vich\GeographicalBundle\VichGeographicalBundle(),
# app/config/config.yml
vich_geographical:
    db_driver: orm   # or mongodb

Annotate the entity — the bundle's core trick is a Doctrine event listener that geocodes a string property and writes lat/lng back to the object:

use Vich\GeographicalBundle\Mapping\Annotation as Vich;

/**
 * @ORM\Entity
 * @Vich\Geographical
 */
class Location
{
    /** @ORM\Column(length=255) @Vich\Geographical(on="update") */
    protected $address;

    /** @ORM\Column(type="decimal", precision=10, scale=7) */
    protected $latitude;

    /** @ORM\Column(type="decimal", precision=10, scale=7) */
    protected $longitude;
}

First use case / first place to look: rendering one map with a handful of markers.

// Controller
$map = $this->get('vich_geographical.map_provider')->getMap();
$map->setZoom(6);
foreach ($locations as $loc) {
    $map->addMarker(new MapMarker($loc, ['title' => $loc->getName()]));
    $map->addWindow(new MapWindow($loc, ['content' => $loc->getAddress()]));
}
return ['map' => $map];
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_KEY"></script>
{{ vich_geographical_render_map(map) }}

Everything else is discovered by reading, in order: README.md → DependencyInjection/Configuration.php (all config keys) → Resources/config/*.xml (service ids) → Map/ (the object model: Map, MapMarker, MapWindow, Coordinate) → Twig/ (the rendering functions).

Laravel translation of the first use case — this is the part worth copying:

trait HasCoordinates
{
    public static function bootHasCoordinates(): void
    {
        static::saved(function ($model) {
            if ($model->isDirty('address')) {
                GeocodeLocation::dispatch($model);   // queued, not inline
            }
        });
    }
}
<x-map :markers="$locations->map->only('lat','lng','name','address')" :zoom="6" />

Implementation Patterns

1. Geocode-on-write, never on-read. The bundle hangs geocoding off Doctrine's postPersist/postUpdate. Copy the lifecycle hook, not the synchronous call: dispatch a job, store the result, and record a geocoded_at / geocode_status column so failures are visible instead of silently leaving null coordinates.

2. Change detection is the whole game. Only re-geocode when the address field actually changed, and beware echo loops — writing latitude/longitude and flushing inside the listener can re-trigger update. In Symfony this was handled by the listener skipping managed-entity no-ops; in Laravel use isDirty() plus saveQuietly().

3. Map as an object graph, built in a service, not in the view. Map → MapMarker → MapWindow is a clean separation: markers carry a geo entity and options (title, icon, draggable), windows carry HTML content. Keep a MapBuilder-style class per screen so the controller stays thin and the map is testable without rendering.

4. Multiple maps per page. Instantiate new Map('locations') with an explicit name/dom-id instead of the shared provider; the JS renderer keys off that id. Render all map JS in one block after the Google Maps script tag, in the same order the maps were created.

5. Extension points actually worth using:

  • Custom MapRendererInterface implementation if you need OpenLayers/Leaflet output instead of Google Maps.
  • Subclass MapMarker / MapWindow to add icon URLs, colors, clusters.
  • Override templates via app/Resources/VichGeographicalBundle/views/... (Symfony2 convention) rather than editing vendor files.
  • A custom JS "walker" if you need to inject extra client-side behaviour per marker.

6. Laravel-side equivalents of each piece:

Bundle concept Laravel equivalent
Doctrine listener + @Vich\Geographical Model trait + saved hook / observer
Map / MapMarker / MapWindow Plain DTOs or JsonResource payloads
vich_geographical_render_map() Blade component or @push('scripts')
map_provider service Container binding / MapFactory
Bundle template override php artisan vendor:publish (or component slots)

7. Testing. Bind a fake geocoder (a stub returning fixed coordinates) in the test container and assert coordinates are persisted from an address. Never let CI hit Google — it is rate-limited, keyed, and non-deterministic. In Laravel: Http::fake().

Gotchas and Tips

  • It is abandoned in all but name. Symfony2 only, PHP 5.x style, no releases you'd want on modern infra. Treat the repo as reference material and a UX pattern, not a dependency.
  • Google Maps API v3 assumptions are stale. The old <script src="http://maps.google.com/maps/api/js?sensor=false"> snippet and keyless geocoding endpoint no longer work; you now need an API key on both the JS API and the Geocoding API, and geocoding results have caching/attribution restrictions under Google's ToS. If you port this, use Leaflet + OSM/Nominatim or a paid geocoder.
  • null coordinates fail loudly in a bad way. A marker with no lat/lng drags the map to (0,0) off the coast of Africa. Always filter markers to rows where both coordinates are present and non-zero before building the map.
  • Precision and type. Use decimal(10,7) (or POINT + a spatial index) — floats lose precision and string casts break - signs in some drivers. If you need "within X km", don't scan haversine in PHP; use ST_Distance_Sphere or a bounding-box prefilter.
  • Geocoder failures are silent. The listener swallows "no result" responses. Log the raw geocoder payload and keep a nullable geocoded_at so you can query for un-geocoded rows in bulk rather than discovering them in the UI.
  • Script ordering is the #1 rendering bug. vich_geographical_render_map() emits inline JS that assumes the Google Maps API is already loaded. Load the API in <head> (or make the renderer wait for DOMContentLoaded), and never render two maps with the same id.
  • Debugging: temporarily disable the listener/subscriber, log the geocoder HTTP call with the exact query string (URL-encoding of accented addresses is a common cause of "not found"), and check the annotation's on="update" vs insert-only semantics.
  • Privacy. Addresses are frequently personal data. Sending every entity save to a third-party geocoder without consent or a retention policy is a GDPR problem; geocode once, store forever, and document it.
  • Config quirks: db_driver must match your actual mapping driver; the bundle registers separate ORM and ODM listeners. If markers never get coordinates, verify the driver value before debugging the geocoder.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons