Skip to content

Upgrading

The package is pre-1.0: minor versions may break. Composer’s caret on 0.x stays within one minor version (^0.11 allows 0.11.* only), so raise the constraint by hand and read the notes below. The full list of changes is in the changelog.

The JS beacon no longer tracks the page load by default. With TrackVisit active every page was counted twice. Re-publish the script — a published copy is not updated by Composer:

Terminal window
php artisan vendor:publish --tag=visits-assets --force

Where the beacon is the only page-view tracking (full-page cache, a separate frontend), turn it back on: window.VisitsConfig = { autoTrackPageView: true }. A { autoTrackPageView: false } you set to avoid double counting can be removed.

Beacon events now carry attribution: UTM/click IDs from the page URL and referrer from document.referrer. The endpoint: '/visits/collect' + window.location.search workaround can be removed; it still works.

Behaviour changes in 0.13.x patch releases:

  • 0.13.2: a tracked request of a signed-in user links only a visitor that has no user yet; a visitor linked to someone else is moved only by Login or Visits::identify().
  • 0.13.3: visits:aggregate skips days older than retention_days. Re-aggregating history (after a schema change, a bug fix) needs --force; on a pruned database don’t use it for pruned days.
  • 0.13.4: the package schedule runs with withoutOverlapping() and onOneServer() — needs a cache store with atomic locks shared by all servers.
  • 0.13.5 / 0.13.9: Overview and Campaigns without ?tenant= default to a tenant that has non-zero rollups.
  • 0.13.6: visits.enabled = false also unregisters the dashboard and whoami, and Visits::whoami() returns [].
  • 0.13.7: an event tracked with inheritFrom joins the visitor’s last session and doesn’t change the visitor’s geo/device.
  • 0.13.8: VisitorIdentified also fires from RecordVisitJob (in the queue) when an authenticated request links a visitor for the first time — listeners run for token-authenticated users too.

No breaking changes.

  • 0.12.0: storage/* is in the default exclude_paths. A published config keeps your own list — add it there.
  • 0.12.1: the first two requests of a new visitor no longer race into a failed job.
  • 0.12.2: browser_language no longer stores *.

Visitor ids are UUIDs. TokenResolver::generate() returns a UUID and visitor_id.format_regex accepts only UUIDs. Ids issued by older versions (40 alphanumeric characters) in cookies, localStorage and mobile apps no longer pass the check: each existing visitor gets a new id and a new Visitor row on their next visit, and the authenticated-user fallback cannot reuse old rows either. To keep old ids working, accept both formats:

config/visits.php
'visitor_id' => [
'format_regex' => '/^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[a-zA-Z0-9]{40})$/i',
],

A published config from an older version still contains the old regex /^[a-zA-Z0-9]{20,64}$/, which rejects every newly generated UUID because of the dashes — every request becomes a new visitor. Update the key.

New: the token_resolver config key.

No breaking changes. New relations: HasVisits::firstVisitorProfile(), latestVisitorProfile(), firstVisitEvent(), Visitor::firstSession(), latestSession().

  • 0.9.0: user_type and eventable_type are written with getMorphClass(). Apps that use Relation::morphMap() have older rows with the full class name, which HasVisits relations no longer match. Update them:

    use App\Models\Order;
    use App\Models\User;
    use Illuminate\Support\Facades\DB;
    DB::table('visit_visitors')->where('user_type', User::class)->update(['user_type' => (new User)->getMorphClass()]);
    DB::table('visit_sessions')->where('user_type', User::class)->update(['user_type' => (new User)->getMorphClass()]);
    DB::table('visit_events')->where('eventable_type', Order::class)->update(['eventable_type' => (new Order)->getMorphClass()]);
  • 0.9.1: visits:seed-demo is available in every environment except production.

  • 0.9.2: automatic tracking works on Laravel 11+ apps configured through bootstrap/app.php. If you registered TrackVisit by hand as a workaround, remove that.

No breaking changes. New: page_views config key.

Schema changes for tables created before 0.7.0:

  • visit_visitors.user_id and visit_sessions.user_id are string (were unsignedBigInteger) — required for UUID/ULID user keys
  • new indexes visit_sessions (visitor_id, ended_at, last_activity_at) and visit_events (type, created_at)
  • the separate migrations adding search_term, route_name and path were merged into the create migrations and deleted — an install that ran them keeps the columns, but its migrations table lists files that no longer exist

No breaking changes. New: authenticated-user fallback in visitor resolution, Visits::identify().

No breaking changes. New: inheritFrom argument of Visits::track().

Identifier renamed from “token” to “id” in everything a client sees:

BeforeAfter
X-Visitor-Token headerX-Visitor-Id
visitor_token input / JSON keyvisitor_id
visits_token cookievisits_visitor_id
visits_token localStorage key (visits.js)visits_visitor_id

Update clients that read or send the old names. Because the cookie and the localStorage key are renamed, every browser gets a new visitor once. Re-publish visits.js (--force).

New: auto_track, visitor_id.format_regex, visit_events.path. Top pages group by path; events recorded before 0.4.0 have no path and are not counted there.

No breaking changes. New columns search_term and route_name (see 0.7 for how they ended up in the create migrations).

  • visit_events.action is renamed to visit_events.name, and every API follows (Visits::track() writes name).
  • visit_events.eventable_id is a string (was unsignedBigInteger).
  • TrackVisit no longer tracks the dashboard and whoami routes.