stevebauman/location
Retrieve a user’s real location by IP in Laravel. Provides a simple facade/service, multiple driver support, caching options, and easy integration with requests, middleware, and geolocation providers for country, city, lat/long, and more.
composer require stevebauman/location
php artisan vendor:publish --provider="Stevebauman\Location\LocationServiceProvider"
config/location.php (e.g., driver: 'maxmind').
'maxmind' => [
'license_key' => env('MAXMIND_LICENSE_KEY'),
'database_path' => storage_path('app/GeoLite2-Country.mmdb'),
],
'ipapi' => [
'token' => env('IPAPI_TOKEN'),
],
use Stevebauman\Location\Facades\Location;
$position = Location::get(); // Returns a `Position` object
$country = $position->countryCode; // e.g., 'US'
$city = $position->city; // e.g., 'San Francisco'
$timezone = $position->timezone; // e.g., 'America/Los_Angeles'
Create middleware to block access by country:
php artisan make:middleware BlockCountry
// app/Http/Middleware/BlockCountry.php
public function handle(Request $request, Closure $next)
{
$position = Location::get();
if (in_array($position->countryCode, ['RU', 'CN'])) {
abort(403, 'Access denied for your region.');
}
return $next($request);
}
Register in app/Http/Kernel.php:
protected $routeMiddleware = [
'block.country' => \App\Http\Middleware\BlockCountry::class,
];
Use in routes:
Route::middleware(['block.country'])->group(function () {
// Protected routes
});
Configure multiple drivers in config/location.php with a fallback chain:
'drivers' => [
'maxmind',
'ipapi',
'ip2location',
],
The package will attempt drivers in order until one succeeds. Log failures for monitoring:
Location::get(); // Auto-falls back if a driver fails
Leverage Laravel’s cache to reduce API calls:
// Cache for 1 hour
$position = Location::get()->remember(3600);
Or configure globally in config/location.php:
'cache_ttl' => 3600, // Default cache duration in seconds
Switch providers based on runtime conditions (e.g., environment):
$driver = app()->environment('production') ? 'ipapi' : 'maxmind';
Location::setDriver($driver);
$position = Location::get();
Auto-set locale/currency in middleware:
// app/Http/Middleware/SetLocale.php
public function handle(Request $request, Closure $next)
{
$position = Location::get();
app()->setLocale($position->countryCode === 'FR' ? 'fr' : 'en');
return $next($request);
}
Create Position objects manually for testing or edge cases:
use Stevebauman\Location\Position;
$position = Position::make([
'countryCode' => 'DE',
'city' => 'Berlin',
'timezone' => 'Europe/Berlin',
]);
Index users by location for geo-search:
// app/Models/User.php
public function toSearchableArray()
{
return [
'name' => $this->name,
'location' => Location::get()->countryCode,
];
}
Query in Tinker:
User::search('John')->where('location', 'US')->get();
Add a custom field to display location data:
// app/Nova/Fields/CountryField.php
public static function resolve($value, $resource, $attribute = null)
{
return Location::get()->countryCode ?? 'Unknown';
}
Expose location data in APIs:
// app/Http/Resources/UserResource.php
public function toArray($request)
{
return [
'name' => $this->name,
'location' => [
'country' => Location::get()->countryCode,
'city' => Location::get()->city,
],
];
}
Process location-based tasks asynchronously:
// Dispatch a job
SendGeoWelcomeEmail::dispatch($user->email, Location::get());
// Job
public function handle()
{
$position = Location::get();
Mail::to($this->email)->send(new WelcomeEmail($position));
}
GeoLite2-Country.mmdb) require manual updates. Use the update command:
php artisan location:update
database_path in config/location.php uses forward slashes or raw strings (e.g., r'C:\path\to\file.mmdb')..tar files in a writable directory (configurable via maxmind.tmp_path).IPAPI_TOKEN in .env; the driver throws exceptions if invalid.config/location.php.cache_ttl: 86400) may serve outdated location data. Balance performance and accuracy.Location::clearCache();
Location::fake() to mock responses, but ensure test coverage for fallback logic:
Location::fake([
'countryCode' => 'US',
'city' => 'New York',
]);
Location::get() in multiple middleware).Position object to downstream middleware to avoid repeated API calls:
// First middleware
$position = Location::get();
return $next($request)->with('geo', $position);
// Second middleware
$position = $request->geo;
Enable debug logging in config/location.php:
'debug' => env('APP_DEBUG', false),
Check logs for driver-specific errors (e.g., API timeouts, invalid responses).
Inspect raw responses from drivers for malformed data:
$position = Location::get(['debug' => true]);
// Check Laravel logs for raw response
| Error | Cause | Solution |
|---|---|---|
Class 'GeoIp2\Database\Reader' not found |
Missing MaxMind PHP extension | Install via pecl install geoip or use the Web Service driver. |
cURL error 28: Connection timed out |
API timeout | Increase timeout in driver config (e.g., ipapi.timeout: 10). |
Invalid license key |
MaxMind key expired | Regenerate key at MaxMind. |
Too many requests |
IPAPI rate limit exceeded | Upgrade plan or implement caching. |
File not found |
MaxMind database missing | Run php artisan location:update. |
Extend the AbstractDriver class to integrate with proprietary services:
// app/Services/CustomLocationDriver.php
use Stevebauman\Location
How can I help you explore Laravel packages today?