PHPackages                             atldays/laravel-geo - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. atldays/laravel-geo

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

atldays/laravel-geo
===================

Retrieve visitor location from IP addresses in Laravel using online and local services, including country, city, continent, and coordinates.

v2.0.0(1mo ago)0361MITPHPPHP ^8.2CI passing

Since Apr 19Pushed 1mo agoCompare

[ Source](https://github.com/atldays/laravel-geo)[ Packagist](https://packagist.org/packages/atldays/laravel-geo)[ Docs](https://github.com/atldays/laravel-geo)[ RSS](/packages/atldays-laravel-geo/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (2)Dependencies (13)Versions (4)Used By (1)

Laravel Geo
===========

[](#laravel-geo)

[![Latest Version on Packagist](https://camo.githubusercontent.com/b56e156a15be4d097c9c5aae230b803d5b97249c78b65a8ff31286b1e37c446e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f61746c646179732f6c61726176656c2d67656f2e7376673f6c6f676f3d7061636b6167697374267374796c653d666f722d7468652d6261646765)](https://packagist.org/packages/atldays/laravel-geo)[![Total Downloads](https://camo.githubusercontent.com/9c49e5614d374fad1040e87471422dd1a6edc1d3786167d769f5c457126f9174/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f61746c646179732f6c61726176656c2d67656f2e7376673f7374796c653d666f722d7468652d626164676526636f6c6f723d626c7565)](https://packagist.org/packages/atldays/laravel-geo/stats)[![CI](https://camo.githubusercontent.com/cb33da3d537f1398f0bcbae3b010ea3aa3112c3c22fe9be14fea614f322c0c9c/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f61746c646179732f6c61726176656c2d67656f2f63692e796d6c3f7374796c653d666f722d7468652d6261646765266c6162656c3d4349)](https://github.com/atldays/laravel-geo/actions/workflows/ci.yml)[![Live](https://camo.githubusercontent.com/223085ca7f0b49a46c570660f1cca78f6025961391e39de19a430dae28053ccf/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f61746c646179732f6c61726176656c2d67656f2f6c6976652e796d6c3f7374796c653d666f722d7468652d6261646765266c6162656c3d4c697665)](https://github.com/atldays/laravel-geo/actions/workflows/live.yml)[![License: MIT](https://camo.githubusercontent.com/7a1226d14a365d288bfe51ece915ee0c7e754a16faa51ff06436504de29b33b4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e7376673f7374796c653d666f722d7468652d6261646765)](LICENSE.md)

`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 `GeoContract`, so you work with consistent DTOs instead of provider-specific arrays.

Drivers
-------

[](#drivers)

The package includes these drivers out of the box:

- [IpApi](https://ip-api.com/)
- [MaxMind](https://www.maxmind.com/)

The default driver is `IpApi`, because it lets developers install the package and see real results immediately.

Installation
------------

[](#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
-----------

[](#quick-start)

### Current request geo data

[](#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 `GeoContract`:

- `Geo::ip()`
- `Geo::provider()`
- `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

[](#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\GeoContract`.

### Dependency Injection

[](#dependency-injection)

You can also resolve the current geo result through dependency injection using `GeoContract`.

```
use Atldays\Geo\Contracts\GeoContract;

class ShowGeoController
{
    public function __invoke(GeoContract $geo)
    {
        return [
            'ip' => $geo->ip(),
            'country' => $geo->country()?->getIsoCode(),
            'city' => $geo->city()?->getName(),
        ];
    }
}
```

Configuration
-------------

[](#configuration)

The main config file is [config/geo.php](config/geo.php).

### Default driver and fallbacks

[](#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:

1. `geo.driver`
2. every class from `geo.fallbacks`

If a driver throws `DriverUnavailableException`, the manager moves to the next configured driver.

Request Macros
--------------

[](#request-macros)

The package registers three request macros:

- `request()->geo()`
- `request()->realIp()`
- `request()->fakeIp()`

`GeoManager::request()` uses:

1. `fakeIp()` when debug mode is enabled and a valid fake IP is present
2. 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)

[IP-API](https://ip-api.com/) 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)

[MaxMind](https://www.maxmind.com/) 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

[](#maxmind-setup-flow)

1. Create or sign in to your MaxMind account.
2. Generate a license key for GeoLite2 downloads.
3. Add `MAXMIND_ACCOUNT_ID` and `MAXMIND_LICENSE_KEY` to your environment.
4. Switch your `geo.driver` to `MaxMind::class` if you want it as the primary driver.
5. 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

[](#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 `.mmdb` file
- 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
--------

[](#geo-data)

Resolved geo data is normalized into `GeoContract`.

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:

- provider
- continent
- country
- city
- registered country
- accuracy radius
- latitude and longitude
- time zone
- postal code
- raw provider payload

Nested geo objects are normalized too:

- `continent()` returns `ContinentContract`It provides a strict name, continent code, and nullable external ID.
- `country()` and `registeredCountry()` return `CountryContract`They provide a strict name, ISO code, normalized continent object, and nullable external ID. They also expose `definition()` for resolving rich country metadata through `CountryDefinitionContract`.
- `city()` returns `CityContract`It provides a strict name, normalized country object, subdivisions collection, and nullable external ID.
- city subdivisions implement `SubdivisionContract`They provide a strict name, ISO code, and nullable external ID.

`provider()` returns the driver name that produced the result, such as `MaxMind` or `IpApi`.

`externalId` is provider-specific metadata. For `MaxMind`, it maps to `geoname_id`. For `IpApi`, it is `null`. It should not be treated as a globally stable cross-provider identifier.

`country()->definition()` resolves rich country metadata through the configured country definition provider.

By default the package points to `Atldays\Geo\CountryDefinitions\Rinvex`, which requires the optional [rinvex/countries](https://packagist.org/packages/rinvex/countries) package to be installed.

Install it when you want to use country definitions:

```
composer require rinvex/countries
```

Config:

```
'definitions' => [
    'country' => \Atldays\Geo\CountryDefinitions\Rinvex::class,
],
```

The configured provider is resolved through `CountryDefinitionManager`, so additional providers can be added later without changing the `CountryContract` API.

Example:

```
$definition = Geo::country()?->definition();

$officialName = $definition?->getOfficialName();
$currencies = $definition?->getCurrencies();
$translations = $definition?->getTranslations();
```

If the configured provider depends on an optional package that is not installed, the package throws `DefinitionUnavailable`.

Some drivers may return partial data. A lookup can still be successful even if only part of the geo payload is available.

Public API
----------

[](#public-api)

Main public package entry points:

- `Atldays\Geo\Facades\Geo`
- `Atldays\Geo\Facades\GeoManager`
- `Atldays\Geo\Contracts\GeoContract`
- `Atldays\Geo\Contracts\DriverContract`
- `Atldays\Geo\Contracts\UpdatableDriverContract`
- `Atldays\Geo\Contracts\UpdateResultContract`

Testing
-------

[](#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
-------

[](#license)

The MIT License (MIT). Please see [LICENSE.md](LICENSE.md) for more information.

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance90

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

50d ago

Major Versions

v1.0.0 → v2.0.02026-04-20

### Community

Maintainers

![](https://www.gravatar.com/avatar/d3d0138ffb1733c52bcd1602597088817ed576f8f9489a3b58095cf5c84264ca?d=identicon)[atldays](/maintainers/atldays)

---

Top Contributors

[![atldays](https://avatars.githubusercontent.com/u/130153594?v=4)](https://github.com/atldays "atldays (30 commits)")

---

Tags

countrygeogeolocationipipapilaravellocationmaxmindphplaravelgeoipgeolocationmaxmindgeoIP APIatldays

###  Code Quality

TestsPHPUnit

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/atldays-laravel-geo/health.svg)

```
[![Health](https://phpackages.com/badges/atldays-laravel-geo/health.svg)](https://phpackages.com/packages/atldays-laravel-geo)
```

###  Alternatives

[spatie/laravel-health

Monitor the health of a Laravel application

88011.3M149](/packages/spatie-laravel-health)[psalm/plugin-laravel

Psalm plugin for Laravel

3325.1M337](/packages/psalm-plugin-laravel)[laravel/ai

The official AI SDK for Laravel.

9782.1M153](/packages/laravel-ai)[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k51.0M7.4k](/packages/larastan-larastan)[spatie/laravel-export

Create a static site bundle from a Laravel app

670139.5k6](/packages/spatie-laravel-export)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9732.3M121](/packages/roots-acorn)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
