Library / SDK
shetabit/visitor avatar
shetabit/visitor

Laravel Visitor: online user detection and visit logs for Laravel apps

a laravel package to work with visitors and retrieve their informations

568 stars78 forksPHPMIT

At a glance

What is it?
shetabit/visitor is a Laravel package that records who visited what, exposes the current visitor's browser, platform, device, language and IP, and answers whether a given user is online right now. It fits teams that need lightweight visit tracking and online presence inside an existing Laravel application, and it does not fit a team that needs a full analytics platform or multi-site reporting.
Who is it for?
Adopt Laravel Visitor if you run a single Laravel application and want visit logs attached to your own models, plus online presence checks, without standing up a separate analytics service. Do not adopt it if you need event funnels, cohorts, or a reporting warehouse, because the package stores visits and answers presence questions, nothing more.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 72 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What shetabit/visitor records and who it is for

Laravel Visitor is a Composer package that extracts and exposes information about the people hitting your application. The README names the fields: `browser`, `ip`, `device` and so on, and adds that in this package you can recognize online users and determine whether a user is online or not.

The audience is an ordinary Laravel team that already has models such as posts or products and wants to know who has viewed them. That is the shape the package fits. It attaches a visit log to a model, keeps a relation for reading those logs back, and can tell you how many distinct visitors a model has had, counted by IP address or by the signed in user.

It is not an analytics suite. There is no dashboard, no funnel, no cohort retention, no data warehouse sync, and no cross-site rollup. The package records and queries, and the reporting on top of it is yours to build with the Laravel query builder.

The version requirements are worth knowing before anything else. The package requires `PHP 8.4+` and supports `Laravel 12+`, so an application on an older PHP or Laravel line has to move first. The project is MIT licensed, and the default branch is `master` with the latest release being v5.0.0 on 2026-08-12, the same day as the last push to the tree.

Two access paths: the request macro and the visitor helper

There are two ways to reach the same API. In a controller you call `$request->visitor()`, and anywhere in the application the `visitor()` helper gives you the same object. The set of accessors on that object is short and fixed: `device` for the device name, `platform` for the platform name, `browser` for the browser name, `languages` for the language name, `ip` for the client address, `request` for the whole request inputs, `useragent` for the raw user agent, and `isOnline` to determine whether the current or a given user is online.

The interaction with your own models is what makes the package more than a request logger. The `visit` method creates a log, and `setVisitor` sets which user the log belongs to.

php
$request->visitor()->browser(); // firefox
$request->visitor()->visit($post); // create log for post
$request->visitor()->setVisitor($user)->visit($post); // create a log which says $user has visited $post

Two traits connect the package to your Eloquent models. `Shetabit\Visitor\Traits\Visitable` goes on any model you want to count visits for, and `Shetabit\Visitor\Traits\Visitor` goes on your `User` class so that user objects can create visit logs and be checked for presence.

php
// or you can save log like the below
visitor()->visit($model);
// or like the below
$model->createVisitLog();

// you can say which user has visited the given $model
$model->createVisitLog($user);
// or like the below
visitor()->setVisitor($user)->visit($model);

Once the trait is applied, the logs are readable through the `visitLogs` relation, which means they are queryable with the same syntax as any other Eloquent relation.

Counting visits, distinct visitors, and reading the relation

Counting is done through the same relation, which keeps the counting logic in the database rather than in PHP. A total is the count of the relation, and distinct counts are expressed by distinct columns.

php
$model->visitLogs()->count();
php
// by ip
$model->visitLogs()->distinct('ip')->count('ip');

// by user's model
$model->visitLogs()->distinct('visitor_id')->count('visitor_id');

The two distinct counts are not interchangeable, and the difference is the interesting part. Counting by IP counts anonymous traffic, and one shared office connection or a carrier NAT will collapse many people into one. Counting by `visitor_id` counts signed in users, and counts nothing for guests. Which number a dashboard should show depends on whether the site is mostly logged in, and the package leaves that choice to the caller.

The `Visitor` trait on the user model gives the same capability from the other direction, so a user object can create its own log, either for the request or for a specific model.

php
$user->visit(); // create a visit log
$user->visit($model); // create a log which says, $user has visited $model

Online detection depends on a user model and a visitor trait

Presence detection is a separate feature with a separate requirement: the `Visitor` trait has to be on your `User` class first. With that in place, you can pull the online users of a given class, and you can ask about one specific user.

php
visitor()->onlineVisitors(User::class); // returns collection of online users
User::online()->get(); // another way

visitor()->isOnline($user); // determines if the given user is online
$user->isOnline(); // another way

The presence of an `online()` scope on the user model is the detail that makes this usable. It means presence is a query condition rather than something you compute in application code, so online users can be filtered, paginated and sorted with the same tools as any other query.

What the presence window actually is, how a visit expires, and whether the check is based on a session, a cache entry or a timestamp, is not spelled out in the README. That matters for sizing: if you intend to ask who is online across a large user base, you need to know the cost of the check and how stale the answer can be, and the configuration file and the `src/` directory are where you would look for those answers rather than the documentation.

The `isOnline` name is also worth reading carefully. It determines whether a user is online, which for an anonymous visitor with no `User` record is a question with no answer, so the feature really applies to the signed in population.

Automatic logging through the LogVisits middleware

Writing `visit()` calls in every controller is tedious, so the package ships a middleware called `LogVisits` that stores logs automatically. The mechanism it uses is route model binding: the middleware stores logs for models that have been bound in the router and that use the `Visitable` trait.

That mechanism has a direct consequence for the middleware ordering. The middleware has to run after Laravel's `SubstituteBindings` middleware, which is what turns a route parameter into a model. Appending it to the `web` group takes care of that, and it is the one piece of setup detail in the README that will bite you if you get it wrong, because a middleware running earlier sees a route parameter string rather than a model, and there is no model to log against.

The practical rule is therefore narrow. You get automatic logs for routes whose parameters resolve to `Visitable` models, and only for those. A page whose data does not come from a bound model will not produce an automatic entry, so the middleware is not a replacement for explicit `visit()` calls, it is a shortcut for the common case.

The middleware class lives at `Shetabit\Visitor\Middlewares\LogVisits`, which is the path to add to your application.

GeoIP enrichment is opt-in and comes with a resolver contract

A visit can carry the location of the address it came from, stored in the `geo_raw` column. This is disabled by default, and enabling it has two parts. You install the `stevebauman/location` package, and you turn the feature on in `config/visitor.php`.

bash
composer require stevebauman/location
php
'geoip' => true,

The bundled resolver hands over the country, the region, the city and the coordinates of a visit. If the bundled behaviour is not what you need, you can write your own by implementing `Shetabit\Visitor\Contracts\GeoIpResolver` and adding it to the `resolvers` array in the configuration file. Having a contract rather than a hard coded provider is the part worth noticing, because it means a team can swap in a different location data source without patching the package.

The default is off, and there is a reason to think about that before switching it on. Geolocation is a second dependency with its own data source, and the location lookup adds work to the request path. It also widens what you store about people, which is a decision to make deliberately rather than by default.

Installation, configuration publishing, and the quality gates

Installation is a single Composer command, and the service provider and the `Visitor` alias are registered by Laravel's package discovery, so there is nothing to add to `config/app.php` or to `bootstrap/providers.php`.

bash
composer require shetabit/visitor

The next step is publishing the migrations and creating the tables. A separate tag publishes the configuration file if you want to change the driver, the name of the table, or the paths that are not logged.

bash
php artisan vendor:publish --tag=visitor-migrations

php artisan migrate
bash
php artisan vendor:publish --tag=visitor-config

On the quality side, every push to `master` and every pull request is checked by GitHub Actions. The test suite runs on PHP 8.4 and 8.5 against Laravel 12 and 13 and against both the lowest and the highest supported dependencies, style is checked with PHP_CodeSniffer, the sources are analysed with PHPStan at level 7 with larastan, and code coverage has to stay above 95 percent. The suite is split into `tests/Unit` for the classes in isolation and `tests/Feature`, which sends real requests through the framework to cover the middleware, the request macro, online visitors and GeoIP enrichment.

The same checks run locally with Composer, or entirely inside a container using the shipped `Dockerfile` and `Makefile`.

bash
composer test           # run the test suite
composer test-coverage  # run the test suite and report code coverage
composer check-style    # check the coding style
composer fix-style      # fix the coding style where possible
composer analyse        # run static analysis
composer ci             # run all of the checks above

The container route means a machine without PHP can still run the suite, and the Makefile takes a PHP version and a Laravel version per invocation, such as `make test PHP_VERSION=8.5` or `make test-laravel LARAVEL=12`.

Editorial conclusion

Adopt Laravel Visitor if you run a single Laravel application and want visit logs attached to your own models, plus online presence checks, without standing up a separate analytics service. Do not adopt it if you need event funnels, cohorts, or a reporting warehouse, because the package stores visits and answers presence questions, nothing more. Before installing, confirm that PHP 8.4 and Laravel 12 are available in your application, then publish the migrations with `php artisan vendor:publish --tag=visitor-migrations` and check what the default `Excluded` paths in `config/visitor.php` leave out of your logs, since the package does not log every request by default.

Frequently asked questions

How do I install the Laravel Visitor package?

Require it with Composer, `composer require shetabit/visitor`, then publish the migrations with `php artisan vendor:publish --tag=visitor-migrations` and run `php artisan migrate`. The service provider and the `Visitor` alias are registered by package discovery, so no manual registration is needed.

What PHP and Laravel versions does Laravel Visitor require?

The package requires `PHP 8.4+` and supports `Laravel 12+`. The CI matrix runs on PHP 8.4 and 8.5 against Laravel 12 and 13.

How do I check whether a user is currently online with Laravel Visitor?

Apply the `Shetabit\Visitor\Traits\Visitor` trait to your `User` class, then call `visitor()->isOnline($user)` or `$user->isOnline()`. To list everyone online, use `visitor()->onlineVisitors(User::class)` or the `User::online()` scope.

How do I count unique visitors of a model in Laravel Visitor?

Count the `visitLogs` relation, using `distinct` on the column that identifies the visitor. The README shows counting by IP with `distinct('ip')->count('ip')` and by signed in user with `distinct('visitor_id')->count('visitor_id')`.

Does Laravel Visitor record visits automatically?

Yes, through the `LogVisits` middleware from `Shetabit\Visitor\Middlewares\LogVisits`. It stores logs for models bound in the router that use the `Visitable` trait, and it has to run after Laravel's `SubstituteBindings` middleware, so appending it to the `web` group is the documented arrangement.

How do I enable geolocation for visits in Laravel Visitor?

Install `stevebauman/location`, publish the configuration with `php artisan vendor:publish --tag=visitor-config`, and set the `geoip` key to `true` in `config/visitor.php`. The bundled resolver returns country, region, city and coordinates, and a custom resolver can be plugged in through the `GeoIpResolver` contract.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. shetabit/visitor on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/shetabit-visitor.svg)](https://hysenlabs.com/projects/shetabit-visitor)

Was this useful?