Route Binding in Laravel
In your Laravel projects, how do you go about setting up routes for a particular resource? Do you do it in the following manner? …
ReadHey! So Livewire 3 and Volt are out. I’ve been playing around with them and they’re pretty cool. My favorite part? Definitely the long polling feature in Livewire.
Together, let’s create a Volt component that shows the live users on the app
To implement this feature, we need a method to track active users. One option is to log active users in a database table. Each entry would have a user_id and a timestamp of when they were active. You can then check activity within the last 5 minutes or any other desired timeframe. Another way is to have a “last_login_at” column in the users table. A third possibility is using a web-sockets server to get a real-time count of active users.
For this article, I’m going to focus on using Redis. I’m fond of Redis because it’s a straightforward in-memory database, yet so powerful. I’ve used this method before, and it’s capable of scaling to handle thousands or even millions of users.
By following these steps, we can easily track the number of active users on our site.
First, we need to install Livewire 3 and Volt. As of writing this article, both are still in beta.
1composer require livewire/livewire "^3.0@beta" # Make sure you have Livewire v3.x installed...
2composer require livewire/volt "^1.0@beta"
After the installation, run the following command to set up Volt:
1php artisan volt:install
With that done, we’re set to dive into building our feature.
Note: I’m assuming that you already have a Laravel application with a users table in the database, and you’ve configured Redis.
To track user activity, we’ll create a middleware that logs each unique user’s activity to Redis every time they load a web page.
First, generate the middleware:
1php artisan make:middleware LogUserActivity
Now, let’s edit the LogUserActivity
middleware:
1namespace App\Http\Middleware;
2
3use Closure;
4use Illuminate\Http\Request;
5use Illuminate\Support\Facades\Redis;
6use Symfony\Component\HttpFoundation\Response;
7
8class LogUserActivity
9{
10 public function handle(Request $request, Closure $next): Response
11 {
12 if ($request->user()) {
13 $userId = $request->user()->id;
14 $duration = 300; // This represents 5 minutes
15
16 Redis::setex("live_users:$userId", $duration, 1);
17 }
18
19 return $next($request);
20 }
21}
In this middleware, we check if there’s an authenticated user. If so, we store a key in Redis with a prefix of “live_users:” followed by the user’s ID.
Lastly, remember to add this middleware to your Kernel.php file to make it active.
1namespace App\Http;
2
3class Kernel extends HttpKernel
4{
5 protected $middlewareGroups = [
6 'web' => [
7 \App\Http\Middleware\EncryptCookies::class,
8 \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
9 \Illuminate\Session\Middleware\StartSession::class,
10 \Illuminate\View\Middleware\ShareErrorsFromSession::class,
11 \App\Http\Middleware\VerifyCsrfToken::class,
12 \Illuminate\Routing\Middleware\SubstituteBindings::class,
13 LogUserActivity::class,
14 ],
15
16 'api' => [
17 // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
18 \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
19 \Illuminate\Routing\Middleware\SubstituteBindings::class,
20 ],
21];
22}
Let’s construct a Volt component to display the number of active users in real-time.
1php artisan make:volt liveusers
This command will generate a file named liveusers.blade.php
inside resources/views/livewire/
.
To clarify the steps, I’ll break down the code explanation into two sections: the PHP code and the HTML content.
Let’s start with the initial lines of our Volt component:
1use function Livewire\Volt\{computed};
2use Illuminate\Support\Facades\Redis;
3
4$liveUsers = computed(function () {
5 $count = 0;
6 $cursor = null;
7 $pattern = 'live_users:*';
8 $batchSize = 1000;
9
10 do {
11 list($cursor, $keys) = Redis::scan($cursor, $pattern, $batchSize);
12 $count += count($keys ?? []);
13 } while ($cursor != 0);
14
15 return $count;
16});
First, we import the necessary functions. The computed function is imported from the Livewire\Volt
namespace along with the Redis facade.
We then use the computed
function to fetch the active user count. This approach is chosen over a typical state
method because it allows for updating the value with long polling
without a full page reload.
Inside the computed callback, we utilize the scan method, a more efficient way to retrieve all keys with the ’live_users:’ prefix. This method is preferred over the keys method when dealing with large data sets, as it allows us to iterate over vast quantities of items without overloading memory.
and that’s the html content for our component
1<div>
2 <div wire:poll.5s>
3 <h3 class="text-base font-semibold leading-6 text-gray-900">Live stats</h3>
4
5 <dl class="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
6 <div class="relative overflow-hidden rounded-lg bg-white px-4 pb-12 pt-5 shadow sm:px-6 sm:pt-6">
7 <dt>
8 <div class="absolute rounded-md bg-indigo-500 p-3">
9 <svg class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
10 stroke="currentColor" aria-hidden="true">
11 <path stroke-linecap="round" stroke-linejoin="round"
12 d="M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z"/>
13 </svg>
14 </div>
15 <p class="ml-16 truncate text-sm font-medium text-gray-500">Live Now</p>
16 </dt>
17 <dd class="ml-16 flex items-baseline">
18 <p class="text-2xl font-semibold text-gray-900">{{ number_format($this->liveUsers, 0) }}</p>
19 </dd>
20 </div>
21 </dl>
22 </div>
23</div>
Notice in line 2 we have wire:poll.5s
to refresh the component every 5 seconds to pull the data. and line 10 gets the computed value we defined and formats it
1number_format($this->liveUsers, 0)
So the entire component would look like
1<?php
2
3use function Livewire\Volt\{state, computed};
4use Illuminate\Support\Facades\Redis;
5
6$liveUsers = computed(function () {
7 $count = 0;
8 $cursor = null;
9 $pattern = 'live_users:*';
10
11 do {
12 list($cursor, $keys) = Redis::scan($cursor, $pattern, 1000);
13 $count += count($keys ?? []);
14 } while ($cursor != 0);
15
16 return $count;
17});
18
19
20?>
21<div>
22 <div wire:poll.5s>
23 <h3 class="text-base font-semibold leading-6 text-gray-900">Live stats</h3>
24
25 <dl class="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
26 <div class="relative overflow-hidden rounded-lg bg-white px-4 pb-12 pt-5 shadow sm:px-6 sm:pt-6">
27 <dt>
28 <div class="absolute rounded-md bg-indigo-500 p-3">
29 <svg class="h-6 w-6 text-white" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
30 stroke="currentColor" aria-hidden="true">
31 <path stroke-linecap="round" stroke-linejoin="round"
32 d="M15 19.128a9.38 9.38 0 002.625.372 9.337 9.337 0 004.121-.952 4.125 4.125 0 00-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 018.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0111.964-3.07M12 6.375a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zm8.25 2.25a2.625 2.625 0 11-5.25 0 2.625 2.625 0 015.25 0z"/>
33 </svg>
34 </div>
35 <p class="ml-16 truncate text-sm font-medium text-gray-500">Live Now</p>
36 </dt>
37 <dd class="ml-16 flex items-baseline">
38 <p class="text-2xl font-semibold text-gray-900">{{ number_format($this->liveUsers, 0) }}</p>
39 </dd>
40 </div>
41 </dl>
42 </div>
43</div>
That’s it for the volt component
For a hands-on demonstration, I’ve set up a console command that simulates user activity in the background. This will randomly mimic active users, letting you see the live count update in real-time on the site.
1collect(Redis::keys('live_users:*'))->each(function ($key) {
2 Redis::del($key);
3});
4
5foreach(range(1, rand(10, 50)) as $userId) {
6 Redis::setex("live_users:$userId", 500, 1);
7}
Happy coding!
In your Laravel projects, how do you go about setting up routes for a particular resource? Do you do it in the following manner? …
ReadWe’ve previously explored the Facade pattern and its functioning within Laravel. However, Laravel doesn’t simply offer a single way to …
Read