geocoder-php/geoip2-provider
GeoIP2 provider for PHP Geocoder using MaxMind GeoLite2/GeoIP2 databases or the paid Precision web service. Geocode IP addresses via a GeoIP2 adapter to return location data (city/country) from MMDB files or API.
composer require geocoder-php/geoip2-provider
use Geocoder\Geocoder;
use Geocoder\Provider\GeoIP2\GeoIP2;
use Geocoder\Provider\GeoIP2\GeoIP2Adapter;
use GeoIp2\Database\Reader;
$reader = new Reader(storage_path('app/GeoLite2-City.mmdb'));
$adapter = new GeoIP2Adapter($reader);
$geocoder = new GeoIP2($adapter);
$ip = '74.200.247.59';
$address = $geocoder->geocodeQuery(\Geocoder\Query\GeocodeQuery::create($ip))->first();
$ip = request()->ip();
$location = $geocoder->geocodeQuery(\Geocoder\Query\GeocodeQuery::create($ip))->first();
$user->update(['country' => $location->getCountry()]);
Caching Results (Critical for performance):
$cache = new \Geocoder\Cache\DoctrineCache(new \Doctrine\Common\Cache\FilesystemCache(storage_path('app/cache')));
$geocoder = new GeoIP2($adapter, $cache);
Cache::remember()) for simplicity.Batch Processing:
$ips = ['1.1.1.1', '2.2.2.2', '3.3.3.3'];
$results = collect($ips)->map(fn($ip) => $geocoder->geocodeQuery(\Geocoder\Query\GeocodeQuery::create($ip))->first());
Integration with Laravel Middleware:
public function handle($request, Closure $next) {
$ip = $request->ip();
$location = $geocoder->geocodeQuery(\Geocoder\Query\GeocodeQuery::create($ip))->first();
$request->merge(['geo_data' => $location]);
return $next($request);
}
public function register() {
$this->app->singleton(Geocoder::class, function ($app) {
$reader = new Reader(storage_path('app/GeoLite2-City.mmdb'));
$adapter = new GeoIP2Adapter($reader);
return new GeoIP2($adapter, $app['cache']);
});
}
// config/geocoder.php
'geoip2' => [
'database_path' => storage_path('app/GeoLite2-City.mmdb'),
];
$reader = new Reader(config('geocoder.geoip2.database_path'));
Database File Permissions:
storage/app/) is writable by the web server.chmod -R 755 storage/ or use storage_path('app') with absolute paths.IPv6 Support:
inet_pton() to validate IPs:
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
// Handle IPv6-specific logic
}
Rate Limiting (Web Service):
Time Zone Data:
DateTimeZone:
$timeZone = $location->getTimeZone();
if (!$timeZone) {
$timeZone = 'UTC'; // Fallback
}
try {
$reader->city($ip); // Throws \GeoIp2\Exception\AddressNotFoundException if invalid
} catch (\Exception $e) {
Log::error("GeoIP2 DB error: " . $e->getMessage());
}
if (!$location->getCountry()) {
Log::warning("No country data for IP: {$ip}");
}
Custom Response Mapping:
GeoIP2Adapter to transform raw GeoIP2 responses:
class CustomGeoIP2Adapter extends GeoIP2Adapter {
public function get($query) {
$result = parent::get($query);
return $result->withCoordinates(
$result->getLatitude() + 0.1, // Offset for testing
$result->getLongitude()
);
}
}
Fallback Providers:
geocoder-php/google-maps-provider) for redundancy:
$geocoder = new \Geocoder\Geocoder();
$geocoder->registerProvider(new GeoIP2($adapter));
$geocoder->registerProvider(new \Geocoder\Provider\GoogleMaps\GoogleMaps());
Environment-Specific Config:
config('app.env') to switch between local (database) and production (API) setups:
$reader = config('app.env') === 'production'
? new \GeoIp2\WebService\Client(env('MAXMIND_ACCOUNT_ID'), env('MAXMIND_LICENSE_KEY'))
: new Reader(storage_path('app/GeoLite2-City.mmdb'));
Reader early (e.g., in a service provider’s boot()) to avoid lazy-loading delays.ip_geolocations) for frequently accessed IPs.How can I help you explore Laravel packages today?