Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

deekepMaks's avatar

The best way to track a user's online status

What's the best way to check a user's online status? I found two options, but I need advice to understand which one is better. The option I found: middleware and broadcast

Middleware simply hangs on all web routes and updates the status on each request:

 if (Auth::check()) {
            $expiresAt = Carbon::now()->addMinutes(1); // keep online for 1 min
            Cache::put('user-is-online-' . Auth::user()->id, true, $expiresAt);
            User::where('id', Auth::user()->id)->update(['last_seen' => (new \DateTime())->format("Y-m-d H:i:s")]);
        }
0 likes
14 replies
Snapey's avatar

You want to add a database write on every request?

deekepMaks's avatar

@Snapey At least the whole Google is filled with such an implementation :)

At a minimum, you can also check for the existence of the cache, then you will not need to write data to the database with each request

But this is what interests me, so I ask knowledgeable people

Snapey's avatar

At a minimum, you can also check for the existence of the cache, then you will not need to write data to the database with each request

Assuming you are only applying this to authenticated routes (and it would be dumb to apply it to ALL routes) then EVERY request by an authenticated user will result in a database write.

Better to write the access into an array in cache and then have a scheduled job that runs every 30 minutes which persists each array entry to the user's record. This way it does not matter if the user loads one page or 300 pages.

It really depends what purpose this information fulfils and how 'up to date' it needs to be. Remember if you need to show other users who is on line then you can do this from the cache, not from the database. Another reason to cache an array - you can iterate over it, wheras the alternative is that you would have to try every user id to see if a cache key exists.

At least the whole Google is filled with such an implementation :)

No, the vast majority of sites do not track last access, and a few record last login.

1 like
deekepMaks's avatar

@Snapey I would like to just store the time when the last activity was made and it doesn't matter that this data is accurate to the second: it can be up to 5 minutes I will check the online status using the cache

Therefore, now my option is to use a middleware that checks for the presence of a cache, if it is not there, then I update the data in the database, if it is, then we just move on. Online status will be cached for 5 minutes

Like that:

middleware:

 if (Auth::check() && !Cache::has('user-is-online-' . Auth::user()->id)) {
            $expiresAt = Carbon::now()->addMinutes(5); // keep online for 5 min
            Cache::put('user-is-online-' . Auth::user()->id, true, $expiresAt);
            User::where('id', Auth::user()->id)->update(['last_seen' => now()]);
        }

model:

public function isOnline() {
		return Cache::has('user-is-online-' . Auth::user()->id);
}

How do you like this approach?

Snapey's avatar

@deekepMaks whats the point of the model code. The only person that can see it is the authenticated user, who is obviously on-line

deekepMaks's avatar

@Snapey You’re right, I was wrong.

But if I need to get a list of users who are online, I can just extract those who have now - last_seen < 5 min. Is it problematic?

deekepMaks's avatar

@Snapey Do you suggest writing each user request to an array in the cache? And then, with a difference in some time, use the last record of the array to update last_seen in the database?

Okay, what is the difference to add an additional condition to check the existence of the cache in middleware, which I wrote about above?

Snapey's avatar

@deekepMaks no

write key of user->id and value of timestamp into the cache, overwriting any value already there

if you put these key => value pairs into an array, then you can get the whole array and iterate over it to see who is online

To be clear, each user would have only one entry in the array in cache

deekepMaks's avatar

@Snapey Okay, I have an entry in the cache of type users-online and it contains an array of user ids I am interested in the following questions:

  1. Let's say that I have a huge number of users online on my site. To check if an entry is in the cache, I need to fetch the entire array and check for the existence of the key. Wouldn't such an operation on a huge array be too time consuming in php itself?
  2. How do I remove from the array of users who have left online? If this was a unique user-online-{id} entry, it would have deleted itself after a while, what to do here?
Snapey's avatar
  1. No, its just a small array (id and timestamp) and you are going directly to the key, not iterating through the array.

  2. you could have a scheduled job to delete old entries every few hours

deekepMaks's avatar

@Snapey Is it possible to immediately get the desired array element from the cache without taking out the entire array? Or do you mean that I need to get the entire array and check the existence of the record as array[key]? Okay, why is the array small? There can be a lot of users who are online

kokleng's avatar

@deekepMaks Bro 👊 , He already told you.

Here:

  1. Use cache to store the user online. How ?
  • We need to store cache of user in every request.
  • Make global middleware to store it
  • Make Cronjob to remove offline user from cache in every #your_time
// example of user data that we have to store in cache
// $arr = [
//     user_id_1 : timestamp,
//     user_id_2 : timestamp,
//     user_id_3 : timestamp,
// ]

// 1. in middleware
$getOldOrNewCached = Cache::has('online-user') ? Cache::get('online-user') : [];

// check if exist user in cached
if(array_key_exists(auth()->id , $getOldOrNewCached)){
    $getOldOrNewCached[auth()->id] = now();
} else {
    array_push($getOldOrNewCached, [auth()->id => now()]);
}
Cache::put('online-user', $getOldOrNewCached); 

// 2. make cronjob or schedule task in laravel to check user active or not in every 5min for example
$getOldOrNewCached = Cache::has('online-user') ? Cache::get('online-user') : [];
$ActiveUser = [];
if(count($getOldOrNewCached) > 0){
    // user inactive for example is user stop browing your website or system in 5min and let find it.
    foreach ($getOldOrNewCached as $key => $value) {
        // we got only online user
        if(now() <= $value + 300){ // 300s = 5min
            array_push($ActiveUser, [$getOldOrNewCached[$key] => now()]);
        }
    }
    Cache::put('online-user', $ActiveUser); 
}

// 3. in your page to get all users who is online
$getOnlineUser =  Cache::get('online-user');
///loop it
// ...
// ...
// ...	 
click's avatar

You don't need cache for this, why don't you make it as simple as this? It only sets the last_seen_at on the user if it was not set or if it more than 5 minutes ago.

if (!$user->last_seen_at || $user->last_seen_at->lessThan(now()->subMinutes(5)) {
    $user->last_seen_at = now();
    $user->save();
}

edit; oh I see this an old thread already. Anyway, I think my solution could help someone else.

2 likes

Please or to participate in this conversation.