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

Geocoder Laravel Laravel Package

toin0u/geocoder-laravel

Geocoder for Laravel integrates the Geocoder PHP library into Laravel apps, enabling address geocoding and reverse geocoding with configurable providers and caching. Requires PHP 8.2+ and Laravel 11+, with service provider auto-discovery for quick setup.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a thin Laravel service-provider wrapper around geocoder-php/geocoder. Its job is to give you one container-resolved object—app('geocoder') / Geocoder::—that can geocode addresses, reverse-geocode coordinates, and resolve IPs, with Laravel-native HTTP and caching underneath.

Minimum path to first successful call:

  1. composer require toin0u/geocoder-laravel (requires PHP ≥ 8.2, Laravel ≥ 11).
  2. Publish config: php artisan vendor:publish --provider="Geocoder\Laravel\Providers\GeocoderService" --tag="config".
  3. Set GOOGLE_MAPS_API_KEY (and GOOGLE_MAPS_LOCALE, which is a Google TLD-style region value, not a language code) in .env.
  4. Hit it:
$addresses = app('geocoder')->geocode('Los Angeles, CA')->get(); // Illuminate\Support\Collection
$coords = $addresses->first()?->getCoordinates();

Read these first, in order: the Upgrading note at the top of the README (13.0.0 is breaking, and the default HTTP adapter changed to LaravelHttpClient), then your published config/geocoder.php (providers/chain order, cache-duration, adapter, reader), then the upstream Geocoder docs for the specific provider you're wiring in. The config file is the whole API surface; almost every surprise lives there.

First real use case: a checkout form or store-locator where you take a free-text address, store lat/lng, and later reverse-geocode a user's IP for a default region. Do that end-to-end before adding more providers.

Implementation Patterns

Wrap it in your own service, don't sprinkle app('geocoder') everywhere. The container binding is convenient but untyped in practice; inject Geocoder\Laravel\ProviderAndDumperAggregator into a small GeocodingService and expose domain methods (coordinatesFor(string $address): ?Coordinates, addressFor(float $lat, float $lng): ?Address). That gives you one place to add logging, metrics, and retries, and one place to swap providers later.

Exploit the Chain provider for resilience. Default config chains GoogleMaps → GeoPlugin. First provider to return wins; a failing provider falls through. You can also pin a provider for a single call:

app('geocoder')->using('google_maps')->geocode($address)->get();

Use the chain for addresses, but pin geo_plugin for IP lookups so you never burn a paid Google call on an IP string.

Treat caching as a first-class feature, not an optimization. Geocoding is a metered, slow, external dependency. Follow the README's dedicated-store recommendation (separate Redis DB → cache.stores.geocode → config/geocoder.php's cache.store) so a cache:clear on your app store doesn't nuke your geocode corpus, and vice versa. On queries where freshness matters, opt out per-call:

app('geocoder')->doNotCache()->geocode($address)->get();

Also add the recommended post-update-cmd/post-install-cmd cache:clear hooks—stale serialized Address collections are a classic post-deploy bug.

Geocode at the edge of your model lifecycle, not in the request path. A model observer on saving that fills lat/lng when the address fields changed is the cleanest pattern; back it with a geocoded_at timestamp so you don't re-hit the API on every save. For bulk imports or user-triggered lookups, push to a queued job—geocoding 500 rows synchronously will blow past your request timeout, especially with retries configured.

Localize deliberately. locale('it')->geocode('Bologna')->get() caches separately per locale, so if you support multiple languages, each locale is a cold cache on day one. If your app switches locale per user, warm the important queries or accept the multiplier on API calls.

Use GeocodeQuery/ReverseQuery when you need options beyond locale—limits, country filters, address components:

use Geocoder\Query\GeocodeQuery;

$query = GeocodeQuery::create('Springfield')
    ->withLocale('en')
    ->withLimit(1);

app('geocoder')->geocodeQuery($query)->get();

Test with Http::fake()—this is the biggest win of the 13.x adapter change. Because LaravelHttpClient delegates to Laravel's Http facade, you can fake provider endpoints and assert what was sent:

Http::fake(['maps.googleapis.com/*' => Http::response($fixture, 200)]);

$result = app('geocoder')->doNotCache()->geocode('Los Angeles, CA')->get();

Http::assertSent(fn ($request) => str_contains($request->url(), 'maps.googleapis.com'));

Run the test suite against the array cache driver (or doNotCache()) so cached results from one test don't leak into the next.

Configure the adapter for production traffic. The default has no timeout—set one, plus retries, in config:

'adapter' => [LaravelHttpClient::class => [
    'timeout' => 10,
    'connectTimeout' => 3,
    'retry' => [3, 100],
]],

Keep verify on; the README's ['verify' => false] example should be treated as a local-debug escape hatch, never a deployed default.

Gotchas and Tips

  • Version numbers are a trap. 13.x is not "nine majors past 5.x"—the major now tracks the highest supported Laravel version. Don't infer Geocoder PHP compatibility from it, and don't blindly composer update across 5.x → 13.x without reading the upgrading section; the default HTTP adapter changed.
  • cache-duration may be silently zero. If you published the config in an older version, the key is missing and caching defaults to 0—everything looks fine but you're hitting the provider every time. Check the key exists and is 9999999 (or your intended value), then php artisan config:clear.
  • Laravel 13 + cache.serializable_classes. The package auto-scans vendor/geocoder-php/* and merges model classes into the allow-list at boot. That is a deliberate, narrow relaxation of a security feature. If your team audits deserialization surface, set auto_register_serializable_classes => false and either maintain the allow-list yourself or disable geocoder caching entirely—otherwise you risk __PHP_Incomplete_Class objects coming back out of cache.
  • GoogleMaps: an API key requires HTTPS. Non-obvious, provider-specific, and the failure mode is a rejected request rather than a helpful error. Also double-check region/locale semantics: Google uses TLDs for region and a fixed language list—wrong values get silently ignored or degrade results.
  • The aggregator carries state. using(), locale(), and doNotCache() mutate the resolved instance. If geocoder is a shared singleton, a doNotCache() in one code path can affect later calls in the same request or in a long-lived queue worker. Keep these calls local to the service method that needs them, or re-resolve.
  • get() returns an empty Collection, not null. Every "address not found" bug starts with calling ->first()->getCoordinates(). Always ->isEmpty() / null-safe chain, and be aware some providers return approximate or multiple matches—withLimit(1) keeps the payload small but you're taking whatever the provider ranks first.
  • Chain fallback hides failures. A misconfigured Google key will quietly produce GeoPlugin-grade results (or nothing), and the chain won't warn you. Log at the service layer when the result is empty or when a non-primary provider answered, at least until you trust the config.
  • Web requests are the wrong place for cold lookups. Even with a 10s timeout, an uncached Google call inside a page render is a latency cliff. Cache-warm via queue on deploy, and consider a "no geocoding during request unless cached" mode using doNotCache()'s inverse (check your cache store first) for critical paths.
  • Extension points worth knowing: any Psr\Http\Client\ClientInterface class in adapter (Curl client with proxy options, or your own retrying decorator); reader in config for file-backed providers like GeoIp2; and adding/removing provider classes in providers—the chain shape is just nested config arrays keyed by class name, so you can nest multiple chains or place providers alongside the chain.
  • env() in the published config is baked at config:cache time. Rotating an API key requires a config recache, not just an .env edit—worth a note in your deploy runbook.
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.
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk