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();
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.
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.
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.
Please do not mislead people. Not everyone uses the same tools you use.
Which part of my answer was mislesding for you?
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
orY
.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.
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.
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.
Using DB as session storage makes your app vulnerable for DoS attacks.
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.
That was usefull thank u so much!!!