studio-vinari/geographical-bundle
Studio Vinari's Geographical Bundle is a Symfony package for managing geographical data such as countries, regions, and cities. It provides extensible Doctrine entities, fixtures, and integration helpers for building address forms, location pickers, and region-aware applications with simple setup.
This is a Symfony bundle, not a Laravel package. The first thing to understand before writing any code is that there is no service provider, no facade, no php artisan vendor:publish, and no auto-discovery. composer require studio-vinari/geographical-bundle in a Laravel app will install some classes and then do absolutely nothing, because the only integration surface is Symfony's DI container and Twig extension system. Your real starting point is not the README but three files in the repo:
DependencyInjection/Configuration.php — the actual config schema (vich_geographical tree: provider, API key, map options).Annotation/*.php — the @Vich\Geographical\Annotation classes that mark entity properties as coordinate/address fields.Twig/ extension + Resources/views/ — the vich_geographical_render(...) helper that emits the map container and JS.Read those and you have read ~90% of the package. Everything else is glue.
Your first use case — the one this bundle exists to solve — is: "a Doctrine entity has latitude/longitude (or an address), and a Twig template renders a map with a marker for it." Reproduce that in Laravel in roughly an hour: an Eloquent model with two decimal columns, a Blade partial that inlines a GeoJSON payload into a data- attribute, and ~30 lines of Leaflet. Do that before deciding you need anything heavier.
If you are actually working in a legacy Symfony app that already depends on this, the getting-started advice is different: pin symfony/* and doctrine/orm to versions the 2017 release supports, freeze the vendor directory, and start planning an exit rather than extending the bundle.
The bundle's model is annotation-driven mapping + template-rendered map. Here is how each piece translates to idiomatic Laravel.
Entity mapping → Eloquent + casts. Where Symfony uses @Vich\Geographical\Annotation\Coordinate on properties, Laravel uses a migration and a cast:
$table->decimal('latitude', 10, 7)->nullable()->index();
$table->decimal('longitude', 10, 7)->nullable()->index();
// model
protected $casts = ['latitude' => 'float', 'longitude' => 'float'];
Keep latitude/longitude as the canonical pair; do not invent a location string column unless you also store a real spatial column.
Map rendering → Blade component, not a helper. The bundle's Twig helper is a god-function that mixes config, entity data, and JS. In Laravel, make a component <x-geo-map :markers="$markers" /> that serializes markers into a data-markers attribute and lets a small Leaflet script hydrate it. Keep the API key in config('services.maps.key') and the JS in a Vite entry point — never inline the key into a Blade template you might render in an email or JSON response.
Address → coordinate → queued job. The bundle's geocoding concept maps cleanly to a GeocodeAddress job using geocoder-php (Google or Nominatim provider), with RateLimited middleware, results cached by address hash, and the result written back to the model. Do not geocode synchronously in a controller; the free tiers and latency will make you regret it.
Admin editing → map picker, not text inputs. The Symfony bundle shipped JS to render maps read-only; the day-to-day need is the inverse — click a point, fill latitude/longitude. Build this as a Livewire or Alpine island bound to the two inputs. Users typing coordinates is the #1 source of garbage data.
Data endpoint → GeoJSON + clustering. For more than a few dozen rows, don't render all markers server-side. Add GET /map/markers returning a FeatureCollection, filter by viewport bbox, and use leaflet.markercluster. This is the pattern that scales, and it's the one the bundle's per-entity render helper cannot express.
Legacy Symfony interop. If you genuinely need the bundle in a Symfony app that shares a database with Laravel, the clean split is: Symfony app owns the entity mapping and writes latitude/longitude; Laravel reads those columns and serves the map. Don't try to shuttle annotations across framework boundaries.
Doctrine\Common\Annotations reader removal, so upgrading PHP or Doctrine will break it in non-obvious ways.decimal(10,7) renders fine but is useless for "within 5km" queries. Prefilter with a bounding box on indexed columns, then Haversine in SQL or in PHP. Reaching for PostGIS/MySQL 8 ST_Distance_Sphere is worth it once you need real spatial filters.lng: 179 and lng: -179.NaN coordinates. Serializing a null latitude into JS yields NaN and silently kills the whole marker layer in some Leaflet versions. Filter server-side (whereNotNull('latitude')->whereNotNull('longitude')) and assert in the component.data-markers payload first, then the browser console for JS include-order errors, then the network tab for a 403 from the tile/API provider. In practice 80% of "map doesn't render" is one of those three.Marker value object — keep rendering decisions out of your Eloquent models so you can swap Leaflet for MapLibre later without touching domain code.geocoder-php for geocoding and a plain Leaflet/MapLibre front end will outlive this repository by a decade.How can I help you explore Laravel packages today?