atldays / laravel-geo
Retrieve visitor location from IP addresses in Laravel using online and local services, including country, city, continent, and coordinates.
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.9
- illuminate/console: ^10.0|^11.0|^12.0|^13.0
- illuminate/contracts: ^10.0|^11.0|^12.0|^13.0
- illuminate/filesystem: ^10.0|^11.0|^12.0|^13.0
- illuminate/http: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
- maxmind-db/reader: ~1.0
- spatie/laravel-data: ^4.21
- spatie/laravel-package-tools: ^1.93
Requires (Dev)
- laravel/pint: ^1.24
- orchestra/testbench: ^8.0|^9.0|^10.0|^11.0
- phpunit/phpunit: ^10.5|^11.5|^12.0|^13.0
This package is auto-updated.
Last update: 2026-04-19 19:59:06 UTC
README
atldays/laravel-geo retrieves visitor location from IP addresses using both online and local services. No matter which provider is used, the result is normalized geo data such as country, city, continent, and coordinates.
You can use it directly from the current request, from any Request instance, or from an explicit IP address.
All supported drivers are normalized into the same strongly typed GeoDataContract, so you work with consistent DTOs instead of provider-specific arrays.
Drivers
The package includes these drivers out of the box:
The default driver is IpApi, because it lets developers install the package and see real results immediately.
Installation
composer require atldays/laravel-geo
Publish the config file if you want to customize driver order or provider settings:
php artisan vendor:publish --tag=laravel-geo-config
Quick Start
Current request geo data
Use the Geo facade when you want data for the current request.
use Atldays\Geo\Facades\Geo; $country = Geo::country(); $city = Geo::city(); $latitude = Geo::latitude(); $payload = Geo::data();
Available facade methods match GeoDataContract:
Geo::ip()Geo::continent()Geo::country()Geo::city()Geo::registeredCountry()Geo::accuracyRadius()Geo::latitude()Geo::longitude()Geo::timeZone()Geo::postalCode()Geo::data()Geo::toArray()
Explicit IP and request lookups
Use the GeoManager facade when you want to resolve a specific IP or request instance.
use Atldays\Geo\Facades\GeoManager; use Illuminate\Http\Request; $byIp = GeoManager::ip('8.8.8.8'); $currentRequest = GeoManager::request(); $request = Request::create('/?ip=8.8.8.8', 'GET'); $geo = GeoManager::request($request); $country = $geo->country(); $city = $geo->city(); $payload = $geo->data();
GeoManager::ip() and GeoManager::request() return Atldays\Geo\Contracts\GeoDataContract.
Configuration
The main config file is config/geo.php.
Default driver and fallbacks
use Atldays\Geo\Drivers\IpApi; return [ 'driver' => IpApi::class, 'fallbacks' => [], ];
You can switch to MaxMind and keep IpApi as a fallback:
use Atldays\Geo\Drivers\IpApi; use Atldays\Geo\Drivers\MaxMind; return [ 'driver' => MaxMind::class, 'fallbacks' => [ IpApi::class, ], ];
Drivers are resolved in this order:
geo.driver- every class from
geo.fallbacks
If a driver throws DriverUnavailableException, the manager moves to the next configured driver.
Request Macros
The package registers two request macros:
request()->geo()request()->realIp()request()->fakeIp()
GeoManager::request() uses:
fakeIp()when debug mode is enabled and a valid fake IP is present- otherwise
realIp()
The fake IP input key is configurable:
'request' => [ 'fake_ip_key' => env('GEO_FAKE_IP_KEY', 'ip'), ],
That makes local testing convenient:
// In debug mode, with GEO_FAKE_IP_KEY=ip // GET /some-page?ip=8.8.8.8 Geo::country();
You can also resolve geo data directly from any Request instance:
use Illuminate\Http\Request; $request = Request::create('/?ip=8.8.8.8', 'GET'); $geo = $request->geo(); $country = $geo->country(); $city = $geo->city();
IP-API
IP-API is the default driver because it gives immediate feedback after installation.
You do not need to create credentials or download a local database to start using it.
If you install the package and keep the default configuration, Geo and GeoManager will already resolve data through IpApi.
Example:
use Atldays\Geo\Facades\Geo; use Atldays\Geo\Facades\GeoManager; $currentCountry = Geo::country(); $byIp = GeoManager::ip('8.8.8.8');
Config:
'ip_api' => [ 'base_url' => env('GEO_IP_API_BASE_URL', 'http://ip-api.com'), 'timeout' => env('GEO_IP_API_TIMEOUT'), ],
This is the recommended choice when you want to try the package quickly without creating external credentials or downloading a local database first.
MaxMind
MaxMind uses a local .mmdb database and is the better choice when you want stable local lookups backed by a real database file.
To use it, you need a MaxMind account and credentials for the GeoLite2 download service.
GeoLite2 is free, so developers can start with the free MaxMind offering and still get a solid local integration.
Config:
'maxmind' => [ 'account_id' => env('MAXMIND_ACCOUNT_ID'), 'license_key' => env('MAXMIND_LICENSE_KEY'), 'edition_id' => env('MAXMIND_EDITION_ID', 'GeoLite2-City'), 'download_url' => env('MAXMIND_DOWNLOAD_URL'), 'database_path' => env('MAXMIND_DATABASE_PATH', storage_path('app/geo/maxmind')), 'database_filename' => env('MAXMIND_DATABASE_FILENAME'), 'metadata_filename' => env('MAXMIND_METADATA_FILENAME', 'metadata.json'), ],
MaxMind setup flow
- Create or sign in to your MaxMind account.
- Generate a license key for GeoLite2 downloads.
- Add
MAXMIND_ACCOUNT_IDandMAXMIND_LICENSE_KEYto your environment. - Switch your
geo.drivertoMaxMind::classif you want it as the primary driver. - Run the update command to download the local database.
Example environment:
MAXMIND_ACCOUNT_ID=your-account-id MAXMIND_LICENSE_KEY=your-license-key
Updating the MaxMind database
Run:
php artisan geo:update
Force a fresh download even if the local file appears current:
php artisan geo:update --force
The updater stores:
- the downloaded
.mmdbfile - a metadata JSON file next to it
UpdateResult is generic and only reports:
- whether the resource was downloaded
- the local stored path
- the metadata path
Source-specific details such as edition_id, download_url, or remote_last_modified live inside metadata instead of the shared DTO.
Geo Data
Resolved geo data is normalized into GeoDataContract.
That means driver-specific payloads are not exposed as arbitrary top-level structures. Every supported driver is mapped into the same typed result shape.
Depending on the driver and available source data, you may receive:
- continent
- country
- city
- registered country
- accuracy radius
- latitude and longitude
- time zone
- postal code
- raw provider payload
Nested geo objects are normalized too:
continent()returnsContinentContractIt provides a strict name and continent code.country()andregisteredCountry()returnCountryContractThey provide a strict name, ISO code, and normalized continent object.city()returnsCityContractIt provides a strict name, normalized country object, and subdivisions collection.- city subdivisions implement
SubdivisionContractThey provide a strict name and ISO code.
Some drivers may return partial data. A lookup can still be successful even if only part of the geo payload is available.
Public API
Main public package entry points:
Atldays\Geo\Facades\GeoAtldays\Geo\Facades\GeoManagerAtldays\Geo\Contracts\GeoDataContractAtldays\Geo\Contracts\GeoDriverAtldays\Geo\Contracts\GeoDriverUpdatableAtldays\Geo\Contracts\UpdateResultContract
Testing
Run the standard test suite:
composer test
Run formatting checks:
composer format:test
Run live driver checks:
composer test:live
Live tests cover real providers and may require external credentials, especially for MaxMind.
License
The MIT License (MIT). Please see LICENSE.md for more information.