syriable/laravel-user-context

User presence, timezone awareness, locale context and login metadata for Laravel — know who is online, where they are, and what time it is for them.

Maintainers

Package info

github.com/syriable/laravel-user-context

pkg:composer/syriable/laravel-user-context

Transparency log

Fund package maintenance!

syriable

Statistics

Installs: 6

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.3 2026-07-19 07:15 UTC

This package is auto-updated.

Last update: 2026-07-19 07:17:13 UTC


README

Latest Version on Packagist GitHub Tests Action Status Total Downloads

Know who is online, where they are, and what time it is for them — presence, timezone awareness, locale context and login metadata for any Laravel app.

A lightweight, database-driven foundation package. No Redis, no heavy dependencies — presence works without a queue worker; geolocation lookups are queued by default so they never block the request path.

$user->isOnline();                       // true
$user->presence()->lastSeen();           // CarbonImmutable|null
$user->location()->countryName();        // "Sweden" (from ISO country code)
$user->timezone()->now();                // CarbonImmutable in the user's zone
$user->greeting();                       // "Good afternoon"

// A user in New York checking the best time to message a user in Shanghai:
$comparison = $newYorkUser->timeFor($shanghaiUser);
$comparison->formattedOffset();          // "+12:00"
$comparison->isNight();                  // true  → maybe wait
$comparison->isConvenientTime();         // false → not a good time to ping

Requirements

  • PHP 8.3+
  • Laravel 11, 12 or 13

Installation

composer require syriable/laravel-user-context

Publish and run the migrations:

php artisan vendor:publish --tag="laravel-user-context-migrations"
php artisan migrate

Optionally publish the config file:

php artisan vendor:publish --tag="laravel-user-context-config"

Add the trait to your authenticatable model:

use Syriable\UserContext\Concerns\HasUserContext;

class User extends Authenticatable
{
    use HasUserContext;
}

Register the tracking middleware so activity is recorded on each request. In bootstrap/app.php (Laravel 11+):

->withMiddleware(function (Middleware $middleware) {
    $middleware->web(append: [
        \Syriable\UserContext\Http\Middleware\TrackUserContext::class,
    ]);
})

That's it — logins, logouts, presence, locale and (optionally) geolocation are now tracked automatically.

Documentation

Publish the config (php artisan vendor:publish --tag="laravel-user-context-config") and read the comments in config/user-context.php for every key. Notable defaults:

Key Default Why
ip.privacy anonymize Store /24 (IPv4) / /48 (IPv6) — not raw IPs
geolocation.driver null No external lookup until you opt into ipinfo / maxmind / ipapi
queue.enabled true Geo lookups never block the request that recorded activity
routes.middleware web, auth, throttle:60,1 Heartbeat is auth-gated and rate-limited
use Syriable\UserContext\Facades\UserContext;

UserContext::isOnline($user);
UserContext::timezoneFor($user)->now();
UserContext::for($user); // ContextSnapshot

Usage at a glance

Presence

$user->isOnline();                       // bool
$user->presence()->status();             // "online" | "offline"
$user->presence()->lastSeen();           // ?CarbonImmutable
$user->presence()->lastLogin();          // ?CarbonImmutable

UserContext::online()->count();          // query builder over online users

Keep a browser tab "online" with the bundled heartbeat component:

<x-user-context::heartbeat />

Presence source

Laravel's database session driver already records last_activity, ip_address and user_agent for the authenticated user on every request. Rather than duplicate that, the package can read presence straight from Laravel's own sessions table. This is controlled by presence.source:

Value Behavior
auto (default) Read from sessions when the database session driver is active and the table exists; otherwise fall back to the package's own columns.
sessions Always read from Laravel's sessions table (requires the database session driver).
table Always read from the package's user_contexts columns — works on every session driver, supports polymorphic user models, and keeps the package's IP privacy modes.

auto targets applications with a single authenticatable type. Presence reads (isOnline(), presence()->lastSeen(), location()->ipAddress(), location()->userAgent()) flow through the active source; login/logout timestamps, timezone, locale and geolocation always come from the package's own table, since Laravel's sessions table does not record them. The package still writes its own columns regardless of this setting, so switching sources never loses data.

Note

In sessions mode location()->ipAddress() returns the raw address Laravel stores on the session row; the user-context.ip.privacy modes (anonymize / hash / discard) apply only in table mode. Multi-guard or polymorphic setups should use the table source, because Laravel's sessions.user_id has no morph type.

Timezone & locale

$user->timezone()->name();               // "Asia/Shanghai" (or null if unknown)
$user->localTime();                      // CarbonImmutable in their timezone
$user->isNight();                        // bool
$user->greeting();                       // localized "Good evening"
$user->locale();                         // "en_US"

// Explicit overrides always win over IP / header detection:
UserContext::overrideTimezone($user, 'Europe/Berlin');
UserContext::overrideLocale($user, 'de');

Location

$user->location()->countryCode();        // "SE"
$user->location()->countryName();        // "Sweden"
$user->location()->city();               // "Stockholm"
$user->location()->ipAddress();          // last known IP (raw in sessions mode)
$user->location()->userAgent();          // last known User-Agent, or null

User-to-user time comparison

$c = $userA->timeFor($userB);

$c->theirTime;            // CarbonImmutable in B's timezone
$c->formattedOffset();   // "+12:00"
$c->dayPeriod;           // DayPeriod::Night
$c->isConvenientTime();  // is it a reasonable local hour to contact B?

Blade components

<x-user-context::user-presence :user="$user" />
<x-user-context::local-time :user="$user" format="H:i" />
<x-user-context::heartbeat />

API

GET /user-context/me returns the authenticated user's context:

{
    "online": true,
    "last_seen": "2026-07-18T10:30:00+00:00",
    "timezone": "Europe/Stockholm",
    "local_time": "15:30",
    "country": "Sweden",
    "locale": "sv_SE"
}

Events

Listen for any of: UserOnline, UserOffline, UserLocationUpdated, UserTimezoneChanged, UserLoginRecorded. See Extending.

Testing

composer test

Changelog

Please see CHANGELOG for what has changed recently.

Credits

License

The MIT License (MIT). Please see License File for more information.