shetabit/visitor
Track and log Laravel visitors: browser, platform, device, IP, languages, user agent, and request data. Use visitor() helper or $request->visitor(). Record visits for any model via Visitable trait, count views/unique visitors, and detect online users.
Installation:
composer require shetabit/visitor
php artisan vendor:publish --provider="Shetabit\Visitor\Provider\VisitorServiceProvider"
php artisan migrate
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"
}
Or globally via helper:
$device = visitor()->device(); // e.g., "iPhone"
Key Initial Steps:
LogVisits middleware to app/Http/Kernel.php for automatic logging.Visitable trait on models to enable visit tracking:
use Shetabit\Visitor\Traits\Visitable;
class Post extends Model {
use Visitable;
}
$request->visitor()->browser(); // "Firefox"
$request->visitor()->platform(); // "Windows"
visitor()->languages(); // ["en-US", "fr"]
// config/visitor.php
'driver' => 'ua-parser', // or 'custom'
visitor()->visit(); // Logs current request
visitor()->visit($post); // Logs visit to a model
$post->createVisitLog(); // Logs via trait
$post->createVisitLog($user); // Attributes to a user
LogVisits to routes/models to auto-log visits:
Route::middleware(['web', 'logVisits'])->get('/posts/{post}', ...);
visitor()->onlineVisitors(User::class); // Collection of active users
User::online()->get(); // Scoped query
visitor()->isOnline($user); // bool
$user->isOnline(); // bool (via Visitor trait)
$post->visitLogs()->count(); // Total visits
$post->visitLogs()->distinct('ip')->count('ip'); // Unique IPs
$post->visitLogs()->where('created_at', '>', now()->subDays(7))->count();
$user->visits()->where('model_type', Post::class)->count();
// app/Observers/VisitLogObserver.php
public function saving($log) {
$log->custom_field = request()->input('custom_field');
}
Shetabit\Visitor\Contracts\VisitorDriver for custom logic (e.g., IP geolocation).Middleware Placement:
LogVisits after auth middleware to avoid logging guest visits twice./admin) by adding to config/visitor.php:
'except' => [
'admin/*',
'api/auth/*',
],
Bot Detection:
isBot() (e.g., curl, Googlebot). Exclude them from analytics:
if (!$request->visitor()->isBot()) {
visitor()->visit($post);
}
Performance:
visitLogs on models with high traffic (e.g., Post::with('visitLogs')->get()). Use lazy loading:
$post->visitLogs()->count(); // Loads only count
ip and user_id in visit_logs table for large datasets:
Schema::table('visit_logs', function (Blueprint $table) {
$table->index('ip');
$table->index('user_id');
});
User-Agent Parsing:
ua-parser) may misclassify edge cases. Test with:
$request->visitor()->useragent(); // Raw string for debugging
Online User Logic:
config/visitor.php):
'online_timeout' => 300, // 5 minutes (seconds)
DB::transaction(function () use ($user) {
$user->update(['last_seen_at' => now()]);
});
\Log::info('Visitor data:', $request->visitor()->toArray());
visit_logs table exists after php artisan migrate.user_id and model_type columns are present for polymorphic relations.LogVisits to isolate issues:
// app/Http/Kernel.php
'logVisits' => false,
Custom Visit Logs:
Shetabit\Visitor\Models\VisitLog to add fields:
php artisan make:model VisitLogExtension --extend=Shetabit\Visitor\Models\VisitLog
php artisan vendor:publish --tag=visitor-migrations
Driver Customization:
class CustomDriver implements VisitorDriver {
public function parse(Request $request) {
return [
'ip' => $request->ip(),
'geo' => $this->getGeoData($request->ip()),
];
}
}
config/visitor.php:
'driver' => 'custom',
Event Listeners:
VisitLog::created(function ($log) {
if ($log->model_type === Post::class) {
event(new PostViewed($log->model));
}
});
GeoIP Stub:
geoip driver, ensure stubs are published:
php artisan vendor:publish --tag=visitor-config
config/visitor.php with your GeoIP database path.Polymorphic Relations:
Visitable have a visits() relation defined:
public function visits() {
return $this->morphMany(VisitLog::class, 'model');
}
Request Data Sanitization:
password) by default. Customize via:
'exclude_request_fields' => ['api_token', 'secret_key'],
$post->visits()->search('browser:Chrome')->count();
visitor()->platform()) to segment tests:
$platform = visitor()->platform();
ABTest::record($platform, 'conversion_rate');
VisitLog::where('created_at', '<', now()->subYears(1))->delete();
visit_logs table size for large-scale apps (consider archiving).How can I help you explore Laravel packages today?