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

Visitor Tracking Bundle Laravel Package

lendable/visitor-tracking-bundle

A Symfony bundle for tracking visitor activity across your application. It captures page views and related request data, helping you build lightweight analytics, audit traffic, and understand usage without relying on a third-party service.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel package. There is no service provider, no facade, no config/visitor_tracking.php. Treat it as a reference implementation first and a dependency second — it was archived in 2020, has zero dependents, and will not receive Symfony 6/7 or PHP 8.2+ compatibility fixes.

Minimal path in a Symfony app (to evaluate the code):

  1. composer require lendable/visitor-tracking-bundle — expect to pin Symfony 4/5-era versions. If Composer refuses, clone the repo and read it instead; that is often the more useful exercise.
  2. Register the bundle in config/bundles.php (or AppKernel::registerBundles() on older skeletons).
  3. Look at DependencyInjection/Configuration.php for the real config tree — the README is thin and the Configuration class is the source of truth for keys like enable/disable toggles, cookie name/lifetime, and which paths to ignore.
  4. Run bin/console doctrine:schema:update --force (or generate a Doctrine migration) — the bundle ships Doctrine ORM entities, not migrations, so you own the schema.
  5. Hit any route and inspect the new visitor/visit rows.

Where to look first:

  • README.md — install steps only.
  • EventListener/ (or EventSubscriber/) — the kernel.request listener is the whole product; this is the file to port.
  • Entity/ — Visitor and Visit-style models and their relations.
  • DependencyInjection/Configuration.php — config keys and defaults.
  • Resources/config/services.* — wiring, listener priority, and which service you can decorate.

First use case: drop-in request logging — one row per request with IP, user agent, referrer, path, and a cookie-backed visitor identity so you can group visits into sessions.

For a Laravel app, the realistic "getting started" is: read the listener, port ~100–200 lines into a middleware + Eloquent models, and skip the dependency entirely.

Implementation Patterns

Kernel listener → middleware. The bundle's core is a kernel.request event subscriber. The direct Laravel translation is a global middleware registered in bootstrap/app.php (Laravel 11) or app/Http/Kernel.php, running after StartSession so you can attach the visitor to the session, and using terminate() for the write so it happens after the response is flushed.

Visitor identity via cookie. The bundle mints a token, sets it as a cookie, and reuses it on subsequent requests to group visits. In Laravel:

$token = $request->cookie('visitor_token') ?? Str::uuid();
Cookie::queue('visitor_token', $token, 60 * 24 * 365);

Keep the token opaque; do not put user IDs in it. Link it to auth()->id() in a column instead, so anonymous → logged-in stitching works without changing the cookie.

Separate visitor from visit. Two tables, one row per visitor (stable identity, first seen, last seen) and one row per request (path, method, referrer, UA, IP, timestamp). This is the pattern worth copying; it keeps the visitor table small and makes retention pruning trivial (DELETE FROM visits WHERE created_at < ...).

Decouple the write. A synchronous insert on every request (including assets, health checks, and bots) is the main performance trap. Mirror the bundle's ignore-list config in a Laravel config file and skip early:

if ($request->is('admin/*', 'horizon/*', 'up') || $request->isMethod('OPTIONS')) {
    return $next($request);
}

Then move the insert into a queued job or at minimum terminate() so the response is not blocked on the DB.

Config-driven behavior. The bundle uses Symfony's config tree; in Laravel put the equivalent in config/visitor-tracking.php with enabled, ignore_paths, track_bots, anonymize_ip, and retention_days, and read it through config(). This is the single biggest maintainability win over hardcoding.

Extension points. In Symfony you would decorate the tracker service or add a listener with a higher priority to enrich the visit (e.g. resolve geo-IP, attach tenant ID). The Laravel analogue is a VisitorResolver interface bound in a service provider, with the middleware depending on the contract — so tests can swap in a fake and you can add enrichment without editing the middleware.

Testing. Use RefreshDatabase and a feature test that hits a route, then asserts exactly one visits row and a stable visitor_id across two requests with the cookie carried over. Assert the ignore-list works, and assert no row is written when tracking is disabled — that's the config regression that bites in production.

Gotchas and Tips

  • Archived and unmaintained. No security patches, no Symfony 6/7 support, no PHP 8.2+ testing. Even inside Symfony, expect to fork. composer audit will flag it or its transitive Doctrine constraints eventually.
  • Zero dependents is a signal. Nobody has battle-tested this in the wild. Budget for reading every line before trusting it.
  • Doctrine, not Eloquent. You cannot composer require this into Laravel and get models. The entities extend nothing useful in an Eloquent context; port the schema, don't try to bridge the ORMs.
  • One insert per request adds up fast. On a site doing 500 rps that's 43M rows/day. Index (visitor_id, created_at) and created_at alone, and ship a scheduled model:prune or a visits:prune command from day one.
  • Proxy IPs. The bundle sees REMOTE_ADDR unless the framework trusts proxies. In Laravel, configure TrustProxies correctly or every visitor resolves to your load balancer's IP and the data is worthless. Same for X-Forwarded-Proto if you branch on scheme.
  • Cookie flags. Secure, HttpOnly, and SameSite=Lax — the last one matters if you also want the cookie on top-level navigations from external referrers. A SameSite=Strict cookie silently fragments visitor sessions.
  • GDPR. IP and user agent are personal data. Gate the middleware on consent where required, anonymize the last octet, and document retention. The bundle predates a lot of this guidance; do not assume it handles it.
  • Symfony config cache. If you do run it in Symfony, remember bin/console cache:clear after editing config — a stale container is the usual "my config change did nothing" cause.
  • Listener priority. If visits aren't being recorded, check the subscriber's priority relative to the router and security listeners; a listener that runs after the response is committed (or throws) fails silently.
  • Debugging. In Symfony, the profiler's events panel shows whether kernel.request fired. In a ported Laravel middleware, DB::listen() plus Log::debug() in handle()/terminate() is faster than guessing.
  • Best move for a Laravel team: skip the dependency. The behavior is a middleware, two migrations, two models, a config file, and a prune command — a day's work, fully owned, no abandoned dependency, and you get queueing and consent gating for free.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect