Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravisit Laravel Package

coderflexx/laravisit

Laravisit is a lightweight Laravel package for tracking website visits without third-party analytics. Log page views, unique visitors, referrers, browsers, devices, and countries, then query Visit models for popular pages and simple reports. Easy middleware setup and customizable tracking.

View on GitHub
Deep Wiki
Context7

Getting Started

Laravisit is a visit/impression counter for Eloquent models: a polymorphic Visit record attached to any model that uses the HasVisits trait (Post, Page, Listing, User…). v2.2.0 is a pure compatibility release — no API changes, so everything below applies to v2.1.0 and v2.2.0 alike.

Minimal setup:

composer require coderflexx/laravisit

php artisan vendor:publish --tag=laravisit      # config/laravisit.php
php artisan vendor:publish --tag=laravisit-migrations  # if migrations aren't auto-loaded
php artisan migrate

Then opt a model in:

use Coderflex\Laravisit\Concerns\CanVisit;
use Coderflex\Laravisit\Concerns\HasVisits;

class Post extends Model implements CanVisit
{
    use HasVisits;
}

First use case — the one you'll write in the first ten minutes:

$post->visit();            // record a visit with defaults
$post->visits()->count();  // total
$post->visitCount();       // cached-friendly shortcut

Where to look first, in this order:

  1. config/laravisit.php — user model/foreign key, IP and user-agent tracking toggles, cache block.
  2. The published migration for laravisits — this is your migration now, not the package's; you own indexes and column types.
  3. Coderflex\Laravisit\Models\Visit and the HasVisits trait — the query scopes (period(), withIp(), withData()) live there.

If you have a config/laravisit.php in an existing app, re-publish and diff it after upgrading — new keys occasionally appear without a major bump.

Implementation Patterns

Record from the controller, read from the view. Keep the write explicit rather than in a model observer; you usually want "a human viewed this page" semantics, not "this row was hydrated".

public function show(Post $post)
{
    $post->visit();

    return view('posts.show', compact('post'));
}

Enrich the visit when the default isn't enough. Chain the fluent builders before visit():

$post->visits()
    ->withIp()
    ->withData(['referer' => request()->headers->get('referer')])
    ->visit();

Deduplicate per session rather than per request. Laravisit records one row per visit() call — it has no built-in debouncing. A cheap guard:

if (! session()->has("visited.post.{$post->id}")) {
    $post->visit();
    session()->put("visited.post.{$post->id}", true);
}

Or move this into a small RecordVisit middleware/action class so every controller doesn't reinvent it.

Dashboard aggregates via period(). Instead of ->whereBetween('created_at', ...) everywhere:

$post->visits()->period('day')->count();
$post->visits()->period('week')->count();
$post->visits()->period('month')->count();

Wrap these in a PostStats value object or a VisitStats service — you will call them from a dashboard, the resource class, and a scheduled report, and you want one place that defines "this month".

Polymorphic by design. Any number of models can be visitable, and a single query can traverse back: Visit::with('visitable')->latest()->paginate(). Use that for an "activity" admin panel instead of a per-model table.

Caching for hot pages. The config ships a cache block (key/prefix/TTL). If you rely on visitCount() on a high-traffic page, prefer the package's cached counter over an ad-hoc Cache::remember("post.{$id}.visits", ...), and make sure the cache is invalidated on visit() rather than only expiring.

Testing. In RefreshDatabase tests, assert the behaviour you actually rely on:

$post->visit();
$this->assertDatabaseCount('laravisits', 1);
$this->assertSame(1, $post->visitCount());

Guard against accidental writes in unrelated feature tests — a visiting page test that later asserts a count will break the moment someone adds $post->visit() to a middleware.

Upgrading to v2.2.0. This is a constraint-only release:

  • illuminate/contracts now allows ^13.0, PHP 8.4 is supported.
  • Transitive dependency coderflexx/laravel-presenter moved to ^2.2 — that's the package that formats visit data for output. If you have your own pinned constraint on laravel-presenter (unlikely, but check composer why coderflexx/laravel-presenter), resolve that before bumping.
  • Dev-only bumps (orchestra/testbench ^11, nunomaduro/collision ^9) only matter if you mirror the package's test stack; they don't affect consumers.

Run composer update coderflexx/laravisit -W and then your test suite. There is nothing to migrate, no config rename, no signature change.

Gotchas and Tips

  • now() inside the config file is a trap. If config/laravisit.php computes a TTL with now()->addDay(), php artisan config:cache serialises that frozen timestamp into bootstrap/cache/config.php and your cache TTL becomes "whatever time it was deployed", i.e. already expired. Use a plain integer/DateInterval, or compute the TTL at the call site. This bites people after the first deploy, not locally.
  • Migrations are published, not loaded from the vendor directory. After upgrading, the package's migration changes will not reach you. Check the changelog for schema changes and hand-write an alter migration if needed. This is also your opportunity to add the indexes below.
  • Index the polymorphic pair. laravisits is queried as (visitable_type, visitable_id) plus created_at. A composite index on those columns turns a full table scan into a range scan the moment you have real traffic; add it in your own migration.
  • IPs behind a proxy. withIp() stores whatever request()->ip() returns. Behind a load balancer that's your balancer unless TrustProxies is configured — a classic "every visit came from 10.0.0.1" bug.
  • Bots and prefetchers inflate the count. Slack unfurls, Lighthouse, and uptime monitors all hit GET endpoints. Filter on user_agent/referer at the call site, or add an is_bot column via a custom withData() payload — cheaper than a queue job later.
  • The table grows forever. There's no pruning command. Plan a scheduled Visit::where('created_at', '<', now()->subMonths(12))->delete() in chunks, or pre-aggregate monthly counts into a post_stats table if you need >1 year of history.
  • visitCount() vs visits()->count(). The former is the cached path, the latter always hits the database. Use the cached one in views and be aware it can lag by up to the configured TTL after a write — never assert on it in a test that reads immediately after visit() unless you've confirmed the cache is bypassed or flushed.
  • Extension points are limited. Custom visit types/metadata go through withData() (a JSON column) — query it with care across MySQL/Postgres/SQLite, or promote hot keys to real columns in your own migration. If you need a fully custom Visit model, subclass and rebind it in the container; be aware the trait's relation resolves from the package's model unless you override it.
  • Compatibility matrix. v2.2.0 supports Laravel 10/11/12/13 and PHP 8.1–8.4. If you're stuck on Laravel 9 or PHP 8.0, stay on the older line — v2.2.0 will simply refuse to install, which is the correct, boring outcome for a support-only release.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource