shetabit/visitor
Track and log Laravel visitor info (IP, browser, device, platform, languages, user agent) via request helper, and attach visit logs to models with a Visitable trait. Includes online user detection and visit counting/uniques by IP or user.
Installation:
composer require shetabit/visitor
php artisan vendor:publish --provider="Shetabit\Visitor\Provider\VisitorServiceProvider"
php artisan migrate
(Laravel 5.5+ auto-registers the service provider; no manual registration needed.)
First Use Case: Access visitor data in a controller:
use Illuminate\Http\Request;
public function show(Request $request) {
$browser = $request->visitor()->browser(); // e.g., "Chrome"
$ip = $request->visitor()->ip(); // e.g., "192.168.1.1"
return response()->json(['visitor' => compact('browser', 'ip')]);
}
Key Entry Points:
visitor()->browser() (global access).$request->visitor()->device() (controller-specific).visitor()->isOnline($user) (blade/views).Workflow:
Visitable trait to models (e.g., Post, Product):
use Shetabit\Visitor\Traits\Visitable;
class Post extends Model {
use Visitable;
}
// Option 1: Automatic (via LogVisits middleware)
Route::middleware(['logVisits'])->group(function () {
Route::get('/posts/{post}', [PostController::class, 'show']);
});
// Option 2: Manual
$post->createVisitLog($user); // Associate with a user
visitor()->visit($post); // Anonymous visit
Integration Tips:
config/visitor.php:
'except' => [
'admin/*',
'api/webhooks/*',
],
$post->createVisitLog($user)->dispatch();
Workflow:
Visitor trait to your User model:
use Shetabit\Visitor\Traits\Visitor;
class User extends Authenticatable {
use Visitor;
}
// Global online users
$onlineUsers = visitor()->onlineVisitors(User::class);
// Model-specific (e.g., users viewing a Post)
$post->visits()->with('visitor')->get();
Patterns:
// Live collaboration
if ($user->isOnline()) {
broadcast(new UserActive($user));
}
$activeUsers = User::online()->take(10)->get();
Setup:
composer require geoip2/geoip2
.env:
VISITOR_GEOIP_ENABLED=true
VISITOR_GEOIP_DATABASE=/path/to/GeoLite2-City.mmdb
$visit = $post->visits()->latest()->first();
$country = $visit->geoip?->country->isoCode; // e.g., "US"
Use Cases:
LogVisits to routes/models:
Route::middleware(['logVisits'])->group(function () {
Route::resource('posts', PostController::class);
});
config/visitor.php:
'except' => [
'password/reset',
'api/orders',
],
Database Overhead:
Product) may slow down with frequent visit() calls.$post->visits()->create([...])->dispatch();
IP Deduplication:
distinct('ip') may not work as expected if IPs are hashed.ip column is stored raw in migrations:
$table->string('ip')->nullable();
GeoIP Performance:
$visit->geoip = Cache::remember("geoip_{$visit->ip}", now()->addHours(1), function () {
return geoip($visit->ip);
});
Middleware Conflicts:
LogVisits may interfere with auth middleware.$middlewareGroups:
'web' => [
\App\Http\Middleware\Authenticate::class,
\Shetabit\Visitor\Middlewares\LogVisits::class, // After auth
],
Visitor Data:
dd($request->visitor()->toArray());
useragent parsing:
dd($request->userAgent());
Online Status:
config/visitor.php:
'online_timeout' => 30, // minutes
dd(visitor()->isOnline($user)); // Returns bool
Logs:
$post->visits()->with('visitor')->latest()->take(5)->get();
$post->visits()->where('ip', $ip)->count();
Custom Visitor Attributes:
Visitor class:
class CustomVisitor extends \Shetabit\Visitor\Visitor {
public function isMobileApp() {
return str_contains($this->useragent, 'MobileApp/');
}
}
AppServiceProvider:
Visitor::macro('isMobileApp', function () {
return $this->isMobileApp();
});
GeoIP Providers:
// config/visitor.php
'geoip' => [
'driver' => 'custom',
'custom' => \App\Services\CustomGeoIP::class,
],
Visit Logs:
Schema::table('visits', function (Blueprint $table) {
$table->string('utm_source')->nullable();
});
visit() method:
$post->createVisitLog($user, [
'utm_source' => $request->input('utm_source'),
]);
Anonymize Data for Compliance:
$table->string('ip_hash')->unique();
public function setIpAttribute($value) {
$this->attributes['ip_hash'] = hash('sha256', $value);
}
Performance Optimization:
if ($request->visitor()->isBot()) {
return response()->json(['error' => 'Blocked']);
}
remember() for frequent queries:
$post->visits()->remember(60)->count();
Testing:
$request->shouldReceive('visitor')->andReturn(
Visitor::fake()->browser('Firefox')->ip('192.168.1.1')
);
$user->shouldReceive('isOnline')->andReturn(true);
How can I help you explore Laravel packages today?