Laravel: How to Show Number/List of Users Currently Online

If you want to show the number or list of users currently online on your website, there are a few ways to do it. In this tutorial, we will show you two of them.

Notice: To be fair, the most accurate would be to use web sockets and transfer that "live" information with tools like Soketi, but it requires way more configuration. In this tutorial, we went for simple options.

So, our actual goal is to save the last activity of the user. We can achieve it with a built-in Laravel session driver or manually with Middleware.


Option 1: Change the Session Driver to "Database"

Create a sessions table and migrate the database.

php artisan session:table
 
php artisan migrate

Update the SESSION_DRIVER value in the environment file to database.

.env

SESSION_DRIVER=database

Now you can retrieve the total online visitor count:

use Illuminate\Support\Facades\DB;
 
function getOnlineUserCount()
{
return DB::table(config('session.table'))->count();
}

Retrieve logged-in users:

use App\Models\User;
use Illuminate\Support\Facades\DB;
 
function getLoggedInUsers()
{
return DB::table(config('session.table'))
->distinct()
->select(['users.id', 'users.name', 'users.email'])
->whereNotNull('user_id')
->leftJoin('users', config('session.table') . '.user_id', '=', 'users.id')
->get();
}

Get active users in the last five minutes:

use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
 
function getActiveUsersInLastMinutes(int $minutes)
{
return DB::table(config('session.table'))
->distinct()
->select(['users.id', 'users.name', 'users.email'])
->whereNotNull('user_id')
->where('sessions.last_activity', '>', Carbon::now()->subMinutes($minutes)->getTimestamp())
->leftJoin('users', config('session.table') . '.user_id', '=', 'users.id')
->get();
}

Option 2: Middleware to Save Last Activity

Add the last_activity column to the users table.

php artisan make:migration add_last_activity_column_to_users_table
Schema::table('users', function (Blueprint $table) {
$table->integer('last_activity')->index();
});

Do not forget to add the last_activity value everywhere you create the user.

User::create([
// ...
'last_activity' => now()->getTimestamp(),
]);

And add it into the fillable array on the User Model:

app/Models/User.php

protected $fillable = [
// ...
'last_activity',
];

Create the UpdateLastActivity Middleware.

php artisan make:middleware UpdateLastActivity

app/Http/Middleware/UpdateLastActivity.php

namespace App\Http\Middleware;
 
use Carbon\Carbon;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
 
class UpdateLastActivity
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if ($user = auth()->user()) {
$user->timestamps = false;
$user->last_activity = now()->getTimestamp();
$user->saveQuietly();
}
 
return $next($request);
}
}

We set $user->timestamps = false only for this call to keep the updated_at field untouched. Otherwise, updated_at and last_activity will refer to the same time, and technically we are not updating the user.

The user model is saved by calling the saveQuietly method not to fire any observers registered with the user model with every request. This would be an unwanted side effect.

Finally, register UpdateLastActivity Middleware.

app/Http/Kernel.php

protected $middlewareGroups = [
'web' => [
// ...
\App\Http\Middleware\UpdateLastActivity::class,
],

Now you can query Users by their activity using Eloquent, disregarding what session driver you use.

User::where(
'last_activity',
'>',
now()->subMinutes(5)->getTimestamp()
)
->get();
avatar

Changing session save handler to DB is always bad idea - the bigger number of requests are made and more busy database is, the bigger chance for a deadlock we have. I would recomend storing session in some in memory sotrage like memcached or redis.

👍 3
avatar

it does make sense but for example my application works on 3 independent aws boxes using load balancer so as far as i know there is no othrr option than session storing in DB. correct me if I'm wrong.

avatar

As I mentioned before you can use both redis or memcached to store sessions that can be shared between EC2 instances or containers running on such. Example config (may be bit stale)

Moreover you can use MongoDB / Dynamo DB to store sessions. So DB session handler is not the only option.

avatar

Please do not mislead people. Not everyone uses the same tools you use.

avatar

Which part of my answer was mislesding for you?

avatar

Throwing bald statements like it "is always bad idea" without any good arguments. If you didn't have a scenario it doesn't mean it is a bad idea. Neither it is a bad idea if you do not understand it.

"X sucks, use Y" is not a good way to put your opinion.

Go read the post title again, and then write how to do the same thing using X or Y.

avatar

So we went down from 'your answer was mislesding' to ' I will teach you how you should behave'. I gave arguments for not using db session handler in orginal post. I can explain if you did not get it.

avatar

Using your primary database for session store is indeed a bad idea - I agree with Jakub fully. When you load balance your app - use Redis and you can still query the active sessions.

avatar

While it may be that is a bad idea, I would disagree that it is always a bad idea, and has it's own place. Although as most missed out, this was used as an example, and not a comparison of different drivers of what is most "right" and most "correct" in your specific use case. Most applications wouldn't even hit the limitations of db driver, but thanks for addressing that.

You're free to use whatever you like.

avatar

Using DB as session storage makes your app vulnerable for DoS attacks.

avatar

I think the majority of folk would completely agree with Jakub's comment - there are more than a few things in Laravel that are now seen as "just because you can, doesn't mean you should" . They may help in local development or rapid prototyping but you wouldn't ever want to use them in a live/production setup. Additionally, I think he provided reasons and solutions fairly and when asked to expand on his initial comments.

avatar
Abdurashid Abdumominov

That was usefull thank u so much!!!

👍 1

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 59 courses (1057 lessons, total 42 h 44 min)
  • 78 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord

Recent Premium Tutorials