willdurand/geocoder
Powerful PHP geocoding library by William Durand. Geocode addresses to coordinates and reverse-geocode lat/long back to locations, with a clean provider-based API. Supports multiple geocoding services, adapters, caching, and easy integration in any project.
Installation
composer require willdurand/geocoder
Add the provider to config/app.php:
'providers' => [
// ...
Geocoder\Provider\GeocoderServiceProvider::class,
],
Basic Setup
Configure providers in config/geocoder.php (auto-generated after installation):
'providers' => [
'google_maps' => [
'key' => env('GOOGLE_MAPS_API_KEY'),
],
'openstreetmap' => [
'host' => 'https://nominatim.openstreetmap.org',
],
],
First Use Case: Reverse Geocoding
use Geocoder\Geocoder;
use Geocoder\Provider\GoogleMapsProvider;
$geocoder = new Geocoder();
$geocoder->registerProvider(new GoogleMapsProvider());
$results = $geocoder->reverse(48.8566, 2.3522); // Latitude, Longitude
foreach ($results as $result) {
dd($result->getCoordinates(), $result->getFormattedAddress());
}
Forward Geocoding (Address → Coordinates)
$results = $geocoder->geocodeQuery('1600 Amphitheatre Parkway, Mountain View, CA');
$coordinates = $results->first()->getCoordinates();
Batch Processing
$addresses = ['Paris', 'Berlin', 'Tokyo'];
$geocoder->batch($addresses, function ($result, $address) {
// Handle each result
});
Fallback Providers
$geocoder->registerProvider(new OpenStreetMapProvider());
$geocoder->registerProvider(new GoogleMapsProvider());
$results = $geocoder->geocodeQuery('Unknown Place');
// Falls back to OpenStreetMap if Google fails.
Caching Results
$geocoder->registerProvider(new GoogleMapsProvider());
$geocoder->registerCache(new \Geocoder\Cache\DoctrineCache());
$results = $geocoder->geocodeQuery('Cached Address');
Laravel Integration (Service Provider)
// app/Providers/GeocoderServiceProvider.php
use Geocoder\Geocoder;
use Geocoder\Provider\GoogleMapsProvider;
class GeocoderServiceProvider extends ServiceProvider {
public function register() {
$this->app->singleton(Geocoder::class, function () {
$geocoder = new Geocoder();
$geocoder->registerProvider(new GoogleMapsProvider());
return $geocoder;
});
}
}
Then inject Geocoder into controllers/services:
public function __construct(private Geocoder $geocoder) {}
API Key Management
.env:
GOOGLE_MAPS_API_KEY=your_key_here
Rate Limiting
$geocoder->registerProvider(new GoogleMapsProvider(['retries' => 3]));
Caching Quirks
Provider-Specific Issues
OVER_QUERY_LIMIT.?format=json&email=your@email.com.Precision Handling
$lat = round($result->getLatitude(), 6);
$lng = round($result->getLongitude(), 6);
Enable Debug Mode
$geocoder->registerProvider(new GoogleMapsProvider(['debug' => true]));
Logs HTTP requests/responses to storage/logs/geocoder.log.
Handle Exceptions
try {
$results = $geocoder->geocodeQuery('Invalid Address');
} catch (\Geocoder\Exception\UnsupportedProvider $e) {
// Fallback logic
} catch (\Geocoder\Exception\NoResult $e) {
// No match found
}
Test Locally
mockery to stub providers in tests:
$mockProvider = \Mockery::mock(GoogleMapsProvider::class);
$mockProvider->shouldReceive('geocode')->andReturn([$mockResult]);
$geocoder->registerProvider($mockProvider);
Custom Providers
Extend Geocoder\Provider\AbstractProvider to integrate with proprietary APIs:
class CustomProvider extends AbstractProvider {
public function geocode($query) {
$response = Http::get('https://api.custom.com/geocode', ['q' => $query]);
return $this->createResultCollection($response->json());
}
}
Result Transformers Modify results before use:
$geocoder->registerProvider(new GoogleMapsProvider());
$results = $geocoder->geocodeQuery('Paris');
$transformed = $results->map(function ($result) {
return [
'address' => $result->getFormattedAddress(),
'confidence' => $result->getConfidence(),
];
});
Laravel Eloquent Integration Add geocoding to models:
// app/Models/Location.php
use Geocoder\Geocoder;
class Location extends Model {
public function resolveRouteBinding($value, $field = null) {
$geocoder = app(Geocoder::class);
$results = $geocoder->geocodeQuery($value);
return $results->first()?->getCoordinates();
}
}
How can I help you explore Laravel packages today?