Use the Geolocation facade — every method is also available on an injected GeolocationManager. The one-liner helpers cover the common cases:
use RoundlyConsulting\Geolocation\DataTransferObjects\Coordinates;
use RoundlyConsulting\Geolocation\Facades\Geolocation;
$location = Geolocation::locateIp('8.8.8.8');
$location = Geolocation::locateRequest(); // auto-detects the client IP
$location?->city; // "Mountain View"
$location?->region; // "California"
$location?->postalCode; // "94043"
$location?->countryIsoCode; // "US"
$location?->timezone; // "America/Los_Angeles" (IP providers only)
$location?->latitude; // 37.4056
$location = Geolocation::locateAddress('1600 Amphitheatre Pkwy, Mountain View');
$location?->city; // "Mountain View"
$location?->timezone; // "" (Google geocoding returns no timezone)
$location?->latitude; // 37.4224
$location = Geolocation::locateCoordinates(new Coordinates(48.1486, 17.1077)); // reverse geocodinglocateRequest() reads the IP from the current request (or the one you pass) and returns null when there is none. Behind a load balancer, configure Laravel’s trusted proxies so the request IP is the visitor’s, not the proxy’s.
Every helper returns null when no provider answers. A Location you do get back always places something — an empty answer ($location->isEmpty(): no address part, no country, 0,0 coordinates) is treated as a miss — though a coarse IP match can still lack a city or a country.
Named queries
Every helper builds a GeolocationQuery and calls locate(). Build the query yourself when you already have one to pass around:
use RoundlyConsulting\Geolocation\DataTransferObjects\Coordinates;
use RoundlyConsulting\Geolocation\DataTransferObjects\GeolocationQuery;
use RoundlyConsulting\Geolocation\Facades\Geolocation;
Geolocation::locate(GeolocationQuery::forIp('203.0.113.7'));
Geolocation::locate(GeolocationQuery::forAddress('Main Street 1, Springfield'));
Geolocation::locate(GeolocationQuery::forCoordinates(new Coordinates(48.1486, 17.1077)));Dependency injection
Every helper works the same on an injected GeolocationManager — type-hint it wherever Laravel resolves dependencies (see DI and actions):
use Illuminate\Http\Request;
use RoundlyConsulting\Geolocation\GeolocationManager;
final class CheckoutController
{
public function show(Request $request, GeolocationManager $geolocation)
{
$country = $geolocation->locateRequest($request)?->countryIsoCode;
// ...
}
}The Location object
| Property | Type | Contents |
|---|---|---|
humanReadable | string | Display label, e.g. a formatted address or “City, Region Country”. |
street | string | Street — filled by Google only; empty for IP providers. |
city | string | City name. |
region | string | Region / state; defaults to ''. |
postalCode | string | Postal code; defaults to ''. |
countryIsoCode | string | ISO country code, e.g. US. |
latitude | float | Latitude. |
longitude | float | Longitude. |
timezone | string | IANA timezone, e.g. America/Los_Angeles — filled by the IP providers only; defaults to ''. |
type | GeolocationType | Ip, Geolocation (Google) or Default (the fallback). |
Location and Distance implement Arrayable and JsonSerializable, so you can return them straight from a controller:
return response()->json($location); // Location is Arrayable + JsonSerializable
$location->toArray();
// ['humanReadable' => ..., 'street' => ..., 'city' => ..., 'region' => ..., 'postalCode' => ...,
// 'countryIsoCode' => ..., 'latitude' => ..., 'longitude' => ..., 'timezone' => ..., 'type' => 'IP']
$location->coordinates(); // a Coordinates value objectBatch lookups
Resolve many IPs at once. The result is keyed by IP, and failures are isolated per item — any exception on one IP becomes null for that IP and the batch carries on. A scope or override chained before batch() applies to every IP:
$results = Geolocation::batch(['8.8.8.8', '1.1.1.1', '203.0.113.7']);
$results['8.8.8.8']?->city; // a Location or null per IP
foreach ($results as $ip => $location) {
// $location is null when that one lookup failed — the batch never aborts
}
// A scope or override chained before batch() applies to every IP
Geolocation::using('maxmind_database', 'ipinfo')->batch($ips);Each item runs through the normal pipeline, cache and events. HTTP providers pace a batch automatically under their rate-limit budget.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.