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 or in config session.php instead of evp

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();

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.