Skip to content

Models & HasVisits

All models are in Fomvasss\Visits\Models and can be replaced through visits.models (Custom models). Relations resolve the related class through the same config. Columns: Database tables.

Table visit_visitors. Bots excluded by default.

MemberTypeDescription
user()MorphToLinked user (user_type/user_id)
sessions()HasManySessions of this visitor
firstSession()HasOne (ofMany)Session with the earliest started_at
latestSession()HasOne (ofMany)Session with the latest started_at
events()HasManyEvents of this visitor
userDisplayName()?stringDisplay name of the linked user through visits.user_display_resolver

Casts: first_seen_at, last_seen_at (datetime), extra_params, device_meta, geo_meta (array), lat, lng (decimal:7), is_bot (bool).

Table visit_sessions. Bots excluded by default.

MemberTypeDescription
visitor()BelongsToVisitor
user()MorphToUser snapshot (user_type/user_id)
events()HasManyEvents of this session
isOpen()boolended_at is null — not yet closed by visits:close-stale-sessions; it may still be past the timeout
userDisplayName()?stringAs on Visitor

Casts: started_at, last_activity_at, ended_at (datetime), extra_params, device_meta, geo_meta (array), lat, lng (decimal:7), page_views_count, duration_seconds (int), is_bot (bool).

Table visit_events. Bots excluded by default. Has created_at only (UPDATED_AT = null).

MemberTypeDescription
TYPE_PAGE_VIEWconst'page_view'
TYPE_ACTIONconst'action'
session()BelongsToSession
visitor()BelongsToVisitor
eventable()MorphToModel passed to Visits::track()

Casts: created_at (datetime), meta (array), is_bot (bool).

Table visit_stats_daily, no timestamps, no bot scope.

ConstantValue
METRIC_VISITORS'visitors'
METRIC_SESSIONS'sessions'
METRIC_PAGE_VIEWS'page_views'
METRIC_CONVERSIONS'conversions'

Casts: date (date), count (int).

Visitor, Session and Event use Fomvasss\Visits\Concerns\ExcludesBotsByDefault, which adds the global scope Fomvasss\Visits\Models\Scopes\WithoutBotsScope (<table>.is_bot = false).

ScopeDescription
withBots()Remove the scope — humans and bots
onlyBots()Bots only
withoutGlobalScope(WithoutBotsScope::class)Same as withBots()

The scope also applies inside relations: $session->events excludes bot events, $event->visitor is null for a bot visitor.

Fomvasss\Visits\Concerns\HasVisits — for models passed to Visits::track() and for user models.

MethodReturnsDescription
visitEvents()MorphManyEvents whose eventable is this model
latestVisitEvent(?string $name = null)MorphOneLatest event by created_at, optionally only with this name
firstVisitEvent(?string $name = null)MorphOneEarliest event by created_at, optionally only with this name
visitorProfiles()MorphManyVisitors linked to this model as their user
firstVisitorProfile()MorphOneLinked visitor with the earliest first_seen_at
latestVisitorProfile()MorphOneLinked visitor with the latest last_seen_at

The MorphOne methods are ofMany() relations — call ->first() or eager-load them. The name filter is applied inside the ofMany subquery, so “latest with this name” is correct even when newer events with other names exist. On the user model visitorProfiles() also enables the authenticated-user fallback.

Morph types are written with getMorphClass(), so Relation::morphMap() aliases match on reads.

Visitor, Session and Event have factories (Fomvasss\Visits\Database\Factories\*) for your own tests; they need fakerphp/faker.

use Fomvasss\Visits\Models\Visitor;
Visitor::factory()->create(); // is_bot = false
Visitor::factory()->bot()->create();