Laravel: How to tracking user IP after they register or login
Tags: laravel, beginners, tutorial, php
Published: 2022-05-01 03:26:15

## Brainstorm

The plan is create a new column in the user table to store the user country code as the source country that user sign up from and the table to store the history of the user ip address for every day or every time user they login into the system.

We will have 2 events to trigger and store the information

- After user registration
- After user login and when user visit the backend for the normal user (Caching to  too many action when they access the page)

## Research

Now, We need to find some libs to help us do this stuff 😎. Because, I'm kind of lazy guy. Just want to take advance and help my productive.

Searching google.com and [github.com](http://github.com) then I found few libs to help us. But I saw that this one clean and the last commit just 19 days ago which mean It is good support from the creator.

[https://github.com/stevebauman/location](https://github.com/stevebauman/location)


![https://github.com/stevebauman/location](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h10o5vg90vp6zbga0ava.png)


## Let's go


### 1. Play around


```bash
composer require stevebauman/location
```

Create a route to test the data first before we go deep into it

```php
use Stevebauman\Location\Facades\Location;

Route::get('/_t', function () {
    if ($position = Location::get()) {
        // Successfully retrieved position.
        return $position;
    } else {
        // Failed retrieving position.
        return ':(';
    }
})->name('test');
```

Result:

```json
{
  "ip": "66.102.0.0",
  "countryName": "United States",
  "countryCode": "US",
  "regionCode": "CA",
  "regionName": "California",
  "cityName": "Mountain View",
  "zipCode": "94043",
  "isoCode": null,
  "postalCode": null,
  "latitude": "37.422",
  "longitude": "-122.084",
  "metroCode": null,
  "areaCode": "CA",
  "timezone": "America/Los_Angeles",
  "driver": "Stevebauman\\Location\\Drivers\\IpApi"
}
```

As you can see, We will base on the data above to create the migration in the Laravel project.

### 2. Migration

Create country_code column to the users table

```bash
php artisan make:migration add_country_code_column_to_users_table
```

```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddCountryCodeColumnToUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('country_code')->nullable()->index();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('country_code');
        });
    }
}
```

Create a history table to store user IP address

```bash
php artisan make:model UserIpAddress -m
```

```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUserIpAddressesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('user_ip_addresses', function (Blueprint $table) {
            $table->id();
            $table->string('ip')->index(); // "66.102.0.0",
            $table->string("countryName")->nullable(); // "United States",
            $table->string("countryCode")->nullable(); // "US",
            $table->string("regionCode")->nullable(); // "CA",
            $table->string("regionName")->nullable(); // "California",
            $table->string("cityName")->nullable(); // "Mountain View",
            $table->string("zipCode")->nullable(); // "94043",
            $table->string("isoCode")->nullable(); // null,
            $table->string("postalCode")->nullable(); // null,
            $table->string("latitude")->nullable(); // "37.422",
            $table->string("longitude")->nullable(); // "-122.084",
            $table->string("metroCode")->nullable(); // null,
            $table->string("areaCode")->nullable(); // "CA",
            $table->string("timezone")->nullable(); // "America/Los_Angeles",
            $table->char('user_id', 26)->index();
            $table->timestamps();

            $table->index(['ip', 'user_id']);
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('user_ip_addresses');
    }
}
```

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class UserIpAddress extends Model
{
    use HasFactory;

    protected $guarded = [];

    public function parseData($data)
    {
        $payload = [];

        $payload["countryName"] = $data["countryName"] ?? false;
        $payload["countryCode"] = $data["countryCode"] ?? false;
        $payload["regionCode"] = $data["regionCode"] ?? false;
        $payload["regionName"] = $data["regionName"] ?? false;
        $payload["cityName"] = $data["cityName"] ?? false;
        $payload["zipCode"] = $data["zipCode"] ?? false;
        $payload["isoCode"] = $data["isoCode"] ?? false;
        $payload["postalCode"] = $data["postalCode"] ?? false;
        $payload["latitude"] = $data["latitude"] ?? false;
        $payload["longitude"] = $data["longitude"] ?? false;
        $payload["metroCode"] = $data["metroCode"] ?? false;
        $payload["areaCode"] = $data["areaCode"] ?? false;
        $payload["timezone"] = $data["timezone"] ?? false;

        return $payload;
    }
}
```

### 3. Store Information with Laravel Events


After few years, I working with [Laravel](https://laravel.com/). The one I loved that the queue and especially that the [Laravel Horizon](https://laravel.com/docs/9.x/horizon) is very powerful when we process parallel the tasks and jobs at the same time.

If you do not have experience about the Laravel Queue. It is a mistake when you working with the Laravel stack 🌝

So, We go into the [Laravel Events](https://laravel.com/docs/9.x/events#main-content). I love Laravel document. They made it very useful, clean and clear. 🎉

After I research I found 2 events we can use it which are `Illuminate\Auth\Events\Registered`, `Illuminate\Auth\Events\Login`. You can check it out in `app/Providers/EventServiceProvider.php` file

```php
<?php

namespace App\Providers;

use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Events\Login;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        Registered::class => [

        ],
        Login::class => [

        ]
    ];

    /**
     * Register any events for your application.
     *
     * @return void
     */
    public function boot()
    {

    }
}
```

 So now, I will create 2 listeners to listen those event to trigger my code and store information.

Let’s read the Laravel event document to learn how to create the listener.

```bash
php artisan make:listener LogUserIpAddressLoginListener
php artisan make:listener LogUserIpAddressRegisteredListener
```

```php
<?php

namespace App\Listeners;

use App\Models\UserIpAddress;
use Illuminate\Auth\Events\Login;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Stevebauman\Location\Facades\Location;

class LogUserIpAddressLoginListener
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  \App\Events\Login  $event
     * @return void
     */
    public function handle(Login $event)
    {
        $user = $event->user;
        if ($user) {
            if ($position = Location::get()) {
                $userIpAddress = new UserIpAddress(
                    UserIpAddress::parseData((array) $position)
                );
                $userIpAddress->ip = $position->ip;
                $userIpAddress->user_id = $user->id;
                $userIpAddress->save();
            }
        }
    }
}
```

```php
<?php

namespace App\Listeners;

use Illuminate\Auth\Events\Registered;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Stevebauman\Location\Facades\Location;

class LogUserIpAddressRegisteredListener
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  \App\Events\Registered  $event
     * @return void
     */
    public function handle(Registered $event)
    {
        $user = $event->user;
        if ($user) {
            if ($position = Location::get()) {
                $user->country_code = $position->countryCode;
                $user->save();
            }
        }
    }
}
```

Then we add 2 listeners into the `EventServiceProvider`

```php
<?php

namespace App\Providers;

use App\Listeners\LogUserIpAddressLoginListener;
use App\Listeners\LogUserIpAddressRegisteredListener;
use Illuminate\Auth\Events\Registered;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
use Illuminate\Auth\Events\Login;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event listener mappings for the application.
     *
     * @var array
     */
    protected $listen = [
        Registered::class => [
            LogUserIpAddressRegisteredListener::class
        ],
        Login::class => [
            LogUserIpAddressLoginListener::class
        ]
    ];

    /**
     * Register any events for your application.
     *
     * @return void
     */
    public function boot()
    {

    }
}
```

Done 🎉

If you want to make it faster and cooler that you can use the Queue (Because for the site which have big traffic and huge users that we can process it later and do not stress on the server). By implements the `Illuminate\Contracts\Queue\ShouldQueue` for those listeners. That’s why I <3 Laravel.

Enjoy the result.

Thanks for reading 👋