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

Laravel Page Visits Laravel Package

jeffersongoncalves/laravel-page-visits

A lightweight Laravel package for tracking page visits in your app. Record visits to routes and pages to see traffic patterns and popular content, without relying on external analytics services. Simple to install, configure, and extend.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install and publish:

    composer require <vendor>/page-visits
    php artisan vendor:publish --tag="page-visits-config"
    php artisan vendor:publish --tag="page-visits-migrations"
    php artisan migrate
    

    On 1.3.0 the migration set includes the new response_time_stats column on page_visit_daily_stats, so a fresh install and an upgrade converge on the same schema.

  2. Look first at three places: the published config (retention window, timezone, tracking toggles), the page_visit_daily_stats migration (now the contract for what your dashboards can read), and the page-visits:aggregate-and-prune command (the only writer of that table).

  3. Schedule the command — nothing aggregates on its own:

    // routes/console.php or bootstrap/app.php
    Schedule::command('page-visits:aggregate-and-prune')->dailyAt('00:15');
    
  4. First use case: a daily traffic panel that reads count next to response_time_stats->p95. One row per day gives you visits and latency in the same query, which is the whole point of the release — no second metrics store, no extra table joins.

Implementation Patterns

Read the JSON blob through a cast, not raw. Add an array (or a small DTO cast) for response_time_stats on the daily-stat model so $stat->response_time_stats['p95'] works and nulls stay null. If you swap the model via config, keep the cast or every consumer breaks.

Treat the column as nullable-by-design. A day with visits but no timed requests is a legitimate null, not a data bug. Guard at the edges:

$stats = $day->response_time_stats;
$p95 = $stats['p95'] ?? null;

Drive alerting off p95, not avg. avg hides the tail that users feel. A cheap regression check compares today's p95 against a rolling baseline of the prior 7 days and posts to your alert channel when it crosses a threshold and count is above a floor — otherwise low-traffic days produce noisy spikes.

Keep a derived column for anything you sort or filter on. JSON columns are painful to index across engines. If you build a "slowest days" report, add your own generated/stored column for p95 (or cache the computed report) rather than scanning JSON on every request.

Backfill deliberately. Existing daily rows keep response_time_stats = null until they are re-aggregated. Re-run the command for historical dates before the retention window closes; once raw visits are pruned, the latency data for those days is unrecoverable and the null is permanent.

Pair it with your own instrumentation when you need more than "page visits". If your middleware only times page GETs, API and asset responses won't appear in the percentiles. Either extend the timed surface or accept that these numbers describe HTML page loads only — state which in the dashboard so nobody compares them to APM latency.

Expose via a thin resource. Wrap the metrics in an API resource or view model so the percentage keys are renamed once (p95 → p95_ms) and consumers never hardcode the package's key names.

Gotchas and Tips

  • The 1.3.0 upgrade is not optional. Skipping vendor:publish --tag="page-visits-migrations" leaves the column missing and the command throws on write. Publish first, then migrate, then run the command once by hand to confirm.
  • Re-publishing migrations may prompt for existing files. Laravel skips files that already exist unless forced; if you force, diff the migration before migrating so you don't silently rewrite an applied migration.
  • Percentiles are SQL-computed, so verify your engine. MySQL has no PERCENTILE_CONT; implementations usually fall back to nearest-rank or interpolation. Confirm which one you're getting — the p99 especially will differ from your APM tool's number, and you don't want to explain that during an incident.
  • Aggregate-then-prune is a single command, and order matters. Re-running it after the raw data is gone rebuilds an empty/partial row. Check whether your retention window comfortably covers any late-arriving or replayed visits before you shorten it.
  • Units are milliseconds. If you ever mix in front-end RUM timings (seconds) into the same field, your p95 becomes meaningless. Normalize at ingest.
  • Timezone changes re-bucket history. Daily rows are cut on the configured timezone; flipping it leaves old rows on the old boundary and new rows on the new one. Set it once, early.
  • Debugging a suspect day: query the raw visits table for that date and count how many rows actually have a timing value. If that count is zero but count is not, the null is correct and the bug is in your middleware, not the aggregator.
  • Testing: use RefreshDatabase, freeze time with Carbon::setTestNow(), seed visits, run the command via $this->artisan(...), then assert both the visit count and the shape of response_time_stats — including the all-null case, which is the one that regresses silently.
  • Extension points worth knowing: the published config (retention, timezone, tracking), swappable model/command bindings if the package exposes them, and your scheduler — which is the right place for a second, cheaper "today so far" aggregation if the nightly-only cadence makes your dashboard too stale.
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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource