Loading...

How to detect Country, City, Locale from Ip Address in Symfony

Great tutorial on how to detect Country, City, Locale from an IP address without using third party service APIs, but instead, using local database

Intro

Developers @MaxMind created a PHP library that enables you to get user's geolocation from the backend using your own local database. There are other methods of doing this, like using an API service, which could be paid or not, or doing it from the browser, but, in this post we will be looking on how to get user's geolocation using only local database and backend in Symfony.

Download the database containing all data that is required for getting user's geolocation

Go to this link and download GeoLite2 City binary file.

When download finishes, extract the .tar.gz archive. In the extracted folder, you will see GeoLite2-City.mmdb file. That is the file that is containing all the data we need.

For consistency, always store this file in projectRoot/db/GeoLite2-City.mmdb

Install MaxMind GeoIP2 PHP API

To be able to read data from the database we just downloaded, we will need the MaxMind GeoIP2 PHP API. To do that, we need to run it from the composer:

composer require geoip2/geoip2

This will install the necessary library which will communicate with the GeoLite2 database.

And now, the fun part, coding

First, we will need to store a path to our database relative to our project root. We usualy store them in the config/services.yaml file, so it should look something like this:

services:
  _defaults:
    bind:
      $geo_db_path: '%kernel.project_dir%/db/GeoLite2-City.mmdb'

When this is done, its time to create our service that will give us information we need. Create GeoLocationService Class and IGeoLocationService Interface

GeoLocationService.php

<?php


namespace App\Service;

use GeoIp2\Database\Reader;
use GeoIp2\Exception\AddressNotFoundException;
use GeoIp2\Model\City;

/**
 * Class GeoLocationService
 * @property Reader reader
 * @package App\Service
 */
class GeoLocationService implements IGeoLocationService
{

    /**
     * GeoLocationService constructor.
     * @param string $geo_db_path
     * @throws \MaxMind\Db\Reader\InvalidDatabaseException
     */
    public function __construct(string $geo_db_path)
    {
        /** @var Reader reader */
        $this->reader = new Reader($geo_db_path);
    }

    /**
     * @param string $ipAddress
     * @return null|City
     * @throws \GeoIp2\Exception\AddressNotFoundException
     * @throws \MaxMind\Db\Reader\InvalidDatabaseException
     */
    public function readCity(string $ipAddress): ?City
    {
        try {
            // if you are in the production environment you can retrieve the
            // user's IP with $request->getClientIp()
            // Note that in a development environment 127.0.0.1 will
            // throw the AddressNotFoundException

            /** @var City $record */
            $record = $this->reader->city($ipAddress);
            return $record;
        } catch (AddressNotFoundException $ex) {
            // If error occured, return null and do logging...
            return null;
        }
    }
}

IGeoLocationService.php

<?php


namespace App\Service;

use GeoIp2\Model\City;

/**
 * Interface IGeoLocationService
 * @package App\Service
 */
interface IGeoLocationService
{

    /**
     * @param string $ipAddress
     * @return null|City
     * @throws \GeoIp2\Exception\AddressNotFoundException
     * @throws \MaxMind\Db\Reader\InvalidDatabaseException
     */
    public function readCity(string $ipAddress): ?City;
}

And now, you can use it in your Controller, Service as

<?php


namespace App\Controller;


use App\Service\IGeoLocationService;
use Exception;
use GeoIp2\Model\City;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

/**
 * Class DefaultController
 * @property IGeoLocationService geoLocationService
 * @package App\Controller
 */
class DefaultController extends AbstractController
{

    /**
     * DefaultController constructor.
     * @param IGeoLocationService $geoLocationService
     */
    public function __construct(IGeoLocationService $geoLocationService)
    {
        $this->geoLocationService = $geoLocationService;
    }

    /**
     * @Route("/", name="pre_authenticate-landing:index")
     * @param Request $request
     * @return Response
     * @throws Exception
     */
    public function index(Request $request)
    {
        /** @var City $city */
        $city = $this->geoLocationService->readCity($request->getClientIp());
    }
}

And thats it! Enjoy knowing where your users come from! :)

Comments (2)

  • October 10, 2019 04:10  

    http://mewkid.net/buy-xalanta/ - Amoxicillin 500mg <a href="http://mewkid.net/buy-xalanta/">Amoxicillin 500 Mg</a> uxi.bksh.dire-solutions.com.qxt.er http://mewkid.net/buy-xalanta/

  • October 10, 2019 03:10  

    http://mewkid.net/buy-xalanta/ - Amoxicillin <a href="http://mewkid.net/buy-xalanta/">Amoxicillin</a> ltn.ocsf.dire-solutions.com.dkh.gb http://mewkid.net/buy-xalanta/

Leave a comment

Dire Solutions Logo Dark Vertical