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 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
Let's go
1. Play around
composer require stevebauman/location
Create a route to test the data first before we go deep into it
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:
{
"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
php artisan make:migration add_country_code_column_to_users_table
<?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
php artisan make:model UserIpAddress -m
<?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
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. The one I loved that the queue and especially that the Laravel 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. 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
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.
php artisan make:listener LogUserIpAddressLoginListener
php artisan make:listener LogUserIpAddressRegisteredListener
<?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
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
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 ๐
Top comments (0)