Как узнать что пользователь находится в Online в Laravel 5.6

Как можно определить, находится ли сейчас пользователь в Online или нет? Решение подходит для последних версий Laravel. Но пример создавался на Ларавел версии 5.4. Итак, как это вообще можно сделать, чисто теоретически? Вариантов, на самом деле, много. Основная идея состоит в том, чтобы положить некоторые данные о пользователе в кэш на пять минут. А потом проверять их, есть ли такие данные в кэше, то пользователь всё ещё в онлайн, если нет, то пользователь покинул сайт.

Итак, приступим к реализации. Для этого нужно создать свой посредник middleware в котором будем записывать данные в кэш:

php artisan make:middleware IsUserOnline

В папке App/Http/Middleware появится новый файл IsUserOnline.php:

<?php
/**
* Check user online.
*/
namespace App\Http\Middleware;
use Closure;
use Auth;
use Carbon\Carbon;
use Cache;
class IsUserOnline
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
{
// If user logged then create cache data on the 5 minutes.
if (Auth::check()) {
$user = Auth::user();
$expiresAt = Carbon::now()->addMinutes(5);
Cache::put('user-is-online-' . $user->id, true, $expiresAt);
}
return $next($request);
}
}

В методе делаем проверку того, является ли пользователь авторизованным. Если является, тогда получаем инстанс этого пользователя. Далее создаём новую дату для хранения кэша:

$expiresAt = Carbon::now()->addMinutes(5);

Берём дату, которая сейчас, и прибавляем к ней пять минут. Для удобной работы с датами есть очень удобная библиотека Carbon. После этого, помещаем в кэш данные, в виде ключ — значение. В виде ключа выступает идентификатор пользователя дописанный к строке:

'user-is-online-' . $user->id

Так будет легче и понятнее получать запись из кэша. Вторым параметром идет булево значение — истина. Но это не суть важно, так как будем проверять именно наличие данной записи в кэше. Третий параметр — это дата, когда истечет валидность этой кэш-записи. В данном конкретном случае, через пять минут запись будет удалена из кэша автоматически.

С посредником закончили, теперь нужно его подключить, чтобы он вызывался при каждом запросе. Для этого в файле App/Http/Kernel.php добавим посредник в группу к другим посредникам:

/**
* The application's route middleware groups.
*
* @var array
*/
protected $middlewareGroups = [
'web' => [
// ...
\App\Http\Middleware\IsUserOnline::class, // Наш посредник.
],
// ...
];

Теперь Middleware будет работать при каждом запросе. Дальше нужно создать метод в модели User:

/**
* Check is user online.
* @return bool
*/
public function isOnline()
{
return Cache::has('user-is-online-' . $this->id);
}

Проверяем, есть ли для вызванного пользователя запись в кэше. Если есть, вернём истину, если нет, то ложь. Больше ничего дописывать не нужно. Атрибут $this->id даст идентификатор пользователя, к которому обращаться.

Дальше остался простой штрих. Прямо шаблона вызвать метод проверки пользователя в онлайн, например так:

@if($user->isOnline())
<span class="color-green font-size-12"><i class="demo-icon icon-circle"></i>
{{ trans('interface.Online') }}</span>
@else
<span class="color-red font-size-12"><i class="demo-icon icon-circle-empty"></i>
{{ trans('interface.Offline') }}</span>
@endif

Здесь всё просто. Вызываем метод через инстанс пользователя:

$user->isOnline()

Если он вернёт истину, то покажем иконку заполненного зеленного кружочка и напишем текст Online. Если ложь, тогда покажем красный пустой кружок и напишем текст Offline.

Идея позаимствована от сюда: https://erikbelusic.com/tracking-if-a-user-is-online-in-laravel/

Есть ещё один способ сделать. Он более прост в реализации, однако создаёт большую нагрузку на базу данных. При большой посещаемости может начаться деградация производительности. Способ заключается в хранении пользовательской сессии в БД. Для начала нужно выполнить две команды:

php artisan session:table 
php artisan migrate

В файле .env переопределите свойство SESSION_DRIVER с file на table. А в модели User нужно создать такой метод:

public function getOnlineAttribute() {
 $activity = DB::table('sessions')->where('user_id',$this->id)->where('last_activity','>',strtotime("-15 minutes"))->count();
 return $activity ? 'Online' : 'Offline' ;
 }