Installation:
composer require yamilovs/sypex-geo
Ensure SxGeoCity.dat (or other Sypex Geo databases) is downloaded from Sypex Geo and placed in your project (e.g., database/ or storage/).
First Use Case: Locate a visitor’s city by IP in a Laravel route or controller:
use Yamilovs\SypexGeo\SypexGeo;
use Yamilovs\SypexGeo\Database\Mode;
$sypexGeo = new SypexGeo(storage_path('app/SxGeoCity.dat'), Mode::FILE);
$ip = request()->ip();
$city = $sypexGeo->getCity($ip);
return response()->json(['city' => $city->name]);
Where to Look First:
SxGeoCity.dat, SxGeoCountry.dat).storage_path() helper for file paths.Service Provider Integration:
Bind the package as a singleton in AppServiceProvider for dependency injection:
public function register()
{
$this->app->singleton(SypexGeo::class, function ($app) {
return new SypexGeo(storage_path('app/SxGeoCity.dat'), Mode::FILE);
});
}
Inject into controllers:
public function __construct(private SypexGeo $sypexGeo) {}
Caching Responses: Cache geo lookups to avoid repeated disk I/O (e.g., using Laravel’s cache):
public function getCachedCity(string $ip): ?array
{
return cache()->remember("geo.city.$ip", now()->addHours(1), function () use ($ip) {
return $this->sypexGeo->getCity($ip)?->toArray();
});
}
Multi-Database Lookups: Chain multiple databases (e.g., country + city) for richer data:
$country = $sypexGeoCountry->getCountry($ip);
$city = $sypexGeoCity->getCity($ip);
return ['country' => $country->name, 'city' => $city->name];
Middleware for Geo-Aware Routes: Attach middleware to enrich requests with geo data:
public function handle(Request $request, Closure $next)
{
$request->merge(['geo' => $this->sypexGeo->getCity($request->ip())]);
return $next($request);
}
Access in routes/controllers:
$city = request()->geo->name;
Command for Database Updates: Create an Artisan command to auto-update databases (if needed):
use Illuminate\Console\Command;
class UpdateGeoDatabase extends Command
{
protected $signature = 'geo:update';
public function handle()
{
// Download latest SxGeoCity.dat and replace old file
}
}
File Paths:
__DIR__.'/SxGeoCity.dat') break in shared hosting or Docker.storage_path() or config:
config(['sypexgeo.path' => storage_path('app/SxGeoCity.dat')]);
Database Updates:
Memory Usage:
SxGeoCountry.dat) may cause high memory usage.Mode::MEMORY_MAPPED for better performance:
$sypexGeo = new SypexGeo($path, Mode::MEMORY_MAPPED);
IPv6 Support:
null.filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)).Case Sensitivity:
strtolower())..dat file is corrupted by testing with a known IP (e.g., Google’s IP):
$sypexGeo->getCity('142.250.190.46'); // Should return Mountain View, USA
try {
$city = $sypexGeo->getCity($ip);
} catch (\Exception $e) {
Log::error("Geo lookup failed for IP $ip: " . $e->getMessage());
}
Custom Data Models:
Extend the City class to add fields (e.g., latitude, longitude):
class ExtendedCity extends \Yamilovs\SypexGeo\City
{
public function getCoordinates(): array
{
return [$this->latitude, $this->longitude];
}
}
Override the getCity() method to return your extended class.
Bulk Lookups: For performance, process IPs in batches (e.g., using queues):
GeoLookupJob::dispatch($ip)->delay(now()->addSeconds(5));
Fallback Logic: Implement fallback to a free API (e.g., ipapi.co) if Sypex Geo fails:
public function getCityWithFallback(string $ip)
{
$city = $this->sypexGeo->getCity($ip);
return $city ?: $this->fetchFromIpApi($ip);
}
Testing:
Mock the SypexGeo class in tests:
$mock = Mockery::mock(SypexGeo::class);
$mock->shouldReceive('getCity')->andReturn((object) ['name' => 'Test City']);
$this->app->instance(SypexGeo::class, $mock);
Configuration:
Centralize paths and modes in config/sypexgeo.php:
return [
'path' => storage_path('app/SxGeoCity.dat'),
'mode' => \Yamilovs\SypexGeo\Database\Mode::MEMORY_MAPPED,
];
Access via config('sypexgeo.path').
How can I help you explore Laravel packages today?