Count active Cache sessions Laravel. (Advice Needed) - php

Running into a problem here and been trying to find a solution for days now.
I want to be able to count the number of Online users in my Laravel application.
I already have build in a system for showing the online and offline user status in my application in the admin panel.
However i am trying to get a counter to just show the numerical value of online users.
Under is the code i used for showing the online offline status.
But now im trying to call while using the count(Cache::has('user-is-online')
However i cant get it working.
Hope somebody can help the way i can call this.
For what i did manage to build i used the following:
Created middleware:
<?php
namespace App\Http\Middleware;
use Closure;
use Auth;
use Cache;
use Carbon\Carbon;
class LastUserActivity
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (Auth::check()) {
$expiresAt = Carbon::now()->addMinutes(1);
Cache::put('user-is-online-' . Auth::user()->id, true, $expiresAt);
}
return $next($request);
}
}
Created controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use DB;
use Cache;
class UserController extends Controller
{
/**
* Show user online status.
*
*/
public function userOnlineStatus()
{
$users = DB::table('users')->get();
foreach ($users as $user) {
if (Cache::has('user-is-online-' . $user->id))
echo "User " . $user->name . " is online.";
else
echo "User " . $user->name . " is offline.";
}
}
}
Created route:
Route::get('/check', 'StatusController#userOnlineStatus');
And i call it with:
<td>
#if(Cache::has('user-is-online-' . $user->id))
<span class="badge badge-warning rounded-0 w-100">Online</span>
#else
<span class="badge badge-danger rounded-0 w-100">Offline</span>
#endif
</td>

Because you've unique key for the each user in the cache. You can't find it by partial key in your case. You can keep a counter like you're doing in the userOnlineStatus function and print the results after that. Look below.
/**
* Show number of online users.
*
*/
public function numberOfOnlineUsers()
{
$users = DB::table('users')->get();
$counter = 0;
foreach ($users as $user) {
if (Cache::has('user-is-online-' . $user->id)) {
$counter++;
}
}
//Passing $counter to the view
return view("your_blade_file_name", ['counter' => $counter]);
}
To access in blade file
The number of online users are {{ $counter }}

Related

Facing strange problem in Laravel Components

I am passing data to the view of the component by $user->profile->profile_pic when I dd in that view it shows the desired value perfectly. But when I use that in some conditions or in tags to print that value it says that Attempt to read property "profile_pic" on null. Although, It is not because I can die and dump that and that value can be seen
Usage of the component:
<x-details
:user="$user->id"
class="w-96 mt-10 py-4"
letter="{{ $user->username[0] }}"
editable="{{ Auth::user()->username == $user->username }}"
profile_pic="{{ $user->profile->profile_pic }}"
/>
The component
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use App\Models\User;
use Illuminate\Support\Facades\DB;
class details extends Component
{
/**
* Create a new component instance.
*
* #return void
*/
public $user;
public function __construct($user = 1)
{
$this->user = $user;
}
/**
* Get the view / contents that represent the component.
*
* #return \Illuminate\Contracts\View\View|\Closure|string
*/
public function render()
{
$user = User::with(['profile'])->firstWhere("id", $this->user);
$pic = $user->profile->profile_pic;
return view('components.details', compact("pic"));
}
}
The view of the component
<div>
#props([
"letter" => "A",
"editable" => 0,
"profile_pic" => 0
])
{{-- #php
$src = "";
if($profile_pic) {
$src = "/uploads/$profile_pic";
} else {
$src = url("fonts/icons/avatars/$letter.svg");
}
#endphp --}}
<div>
{{-- #dd($pic) --}}
{{ $pic }}
{{-- #if(!$editable)
#else
<form id="fileUpload">
<input class="hidden" type="file" name="upload_pic" id="upload_pic">
</form>
#endif --}}
</div>
</div>
It's a common issue when you trying to dd() something in foreach, it will always dump first item only and die, so you are always confirm first item and think it work as well as expected.
In your case, there is probably some user doesn't have profile_pic or profile don't have any profile_pic related on it.
Try to use the code below to debug with it in your component.
public function render()
{
try {
$user = User::with(['profile'])->firstWhere("id", $this->user);
$pic = $user->profile->profile_pic;
return view('components.details', compact("pic"));
} catch (Exception $e) {
dd($user->profile);
}
}
Inside the component, you should use $this:
So instead of
$pic = $user->profile->profile_pic
You should do
$pic = $this->user->profile->profile_pic

What makes this Laravel permissions-checking middleware fail?

I am working on a blogging application in Laravel 8.
The application gives the users rights by assigning them roles. Every role has a set of permissions. There is a many-to-many relationship between roles and permissions.
In the user-rights view, I output each user's permissions successfully:
#foreach ($user->role->permissions as $permission)
<span class="badge bg-primary">{{ $permission->slug }}</span>
#endforeach
The goal
I am trying to restrict access to the Site settings section of the application, like this:
// Settings routes
Route::group(['prefix' => 'settings', 'middleware' => ['checkUserPermissions:edit-settings']], function() {
Route::get('/', [SettingsController::class, 'index'])->name('dashboard.settings');
});
For this purpose, I have created the checkUserPermissions middleware:
class CheckUserPermissions
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* #return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
// Permissions checker
public function hasPermissionTo($permission) {
return in_array($permission, Auth::user()->role->permissions->toArray());
}
public function handle(Request $request, Closure $next, ...$permissions)
{
// Check user permissions
foreach ($permissions as $permission) {
if (!$this->hasPermissionTo($permission)) {
$permission_label = join(' ', explode('-', $permission));
return redirect()->back()->with('error', 'You do not have permission to ' . $permission_label);
}
}
return $next($request);
}
}
The problem
Although the super admin does have the permission to edit settings, dd(in_array($permission, Auth::user()->role->permissions->toArray())) returns false.
That means that the restriction(s) apply when they should not.
NOTE
dd(Auth::user()->role->permissions->toArray()) returns:
Questions
What causes this bug?
What is the easiest fix?
In your custom middleware, you need to compare the permission to the slugs
return in_array($permission, Auth::user()->role->permissions()->pluck('slug')->toArray());
Since you're eager loading permissions, Eqloquent will return a collection of models. Inside of the Collection instance, there is a pluck method that will create a new collection of columns.
Using the toArray method, you will then end up with the expected array that your $permission will match to:
Auth::user()->role->permissions->pluck('slug')->toArray()
An alternative way would be to use PHP native methods (array_column) to achieve this. You could then return a list back to the view of all of the missing permissions as apposed to a single missing permission. This is untested but should just work out the box:
public function handle(Request $request, Closure $next, ...$permissionsToTest)
{
$permissions = array_column(Auth::user()->role->permissions->toArray(), 'slug');
$results = array_map(fn(string $permission): bool => in_array($permission, $permissions), $permissionsToTest);
$failedResults = array_keys(array_filter($results, fn(bool $result): bool => !$result));
$missingPermissions = array_map(fn(int $key): string => 'You do not have permission to ' . implode(' ', explode('-', $permissionsToTest[$key])), $failedResults);
# Utilise the built-in "errors"
return empty($missingPermissions) ? $next($request) : redirect()->back()->with('errors', $missingPermissions);
}
For a vanilla PHP mock-up example, See it 100% working over at 3v4l.org

How can I show items belonging to a logged in user in Laravel?

I want to show the emails that the logged in user has sent from the database
This is the route code:
Route::get('/myEmails/{id}','PagesController#myEmailsShow');
This is the function in the controller:
public function myEmailsShow($id)
{
$myEmails = DB::table('emails')->where('user_id',$id)->get();
return view('content.myEmails', compact('myEmails'));
}
This is the a link where the user click to open the page:
#if(Auth::check())
<a class="nav-link text-white" href="/myEmails/{id}"> my emails</a>
#endif
And here where i want to show the data (i am showing only the name for test):
<div class="row">
#foreach($myEmails as $myEmail)
{{$myEmail->name}}
#endforeach
</div>
I think the best way to accomplish your goals here would be using a hasMany relationship between User and Emails (if emails is a Model).
//User.php
public function emails()
{
return $this->hasMany('App\Models\Email');
}
In the controller, apply the Auth middleware to the myEmailsShow method in a constructor:
//PagesController.php
public function __construct()
{
$this->middleware('auth')->only(['myEmailsShow']);
}
Then, in your myEmailsShow method, do something like the following:
//PagesController.php
public function myEmailsShow()
{
// Middleware Eliminates the need for ID in the function.
$user = auth()->user();
$myEmails = $user->emails;
return view('content.myEmails', compact('myEmails'));
}
You can remove the ID parameter from the route and just make it something like Route::get('/myEmails', 'PagesController#myEmailsShow');. Only users who are logged in will be able to access this page, and they will only see emails belonging to them.
Route::get('/myEmails/{user}','PagesController#myEmailsShow')->name('myemails');
with the controller
use App\Email;
use App\User;
public function myEmailsShow(User $user)
{
///calling the model Email at parameters instead of $id eloquent automatically the data from DB
$myEmails = Email::where('user_id',$user->id)->get();
return view('content.myEmails')->with('myEmails', $myEmails);
}
The link has little modifications
#if(Auth::check())
<a class="nav-link text-white" href="{{route('myemails', $user->id)}}"> my emails</a>
#endif
displaying the value
#foreach($myEmails as $myEmail)
{{$myEmail->name}}
#endforeach

Laravel #can blade policy check up

I'm writing blade policy for a drop down menu in laravel using #can.
<li>
<i class="zmdi zmdi-assignment-o"></i><span>Models</span>
<ul class="ml-menu">
#can('index',App\Model1::class)
<li>
{{__('Model1 Manger')}}
</li>
#endcan
#can('index',App\Model2::class)
<li>
{{__('Model2 Manager')}}
</li>
#endcan
</ul>
</li>
My question is how to hide the 'Model' option from the menu If user don't have permisssion to access model1 and model2?
You can use can within an if statement similar to below:
#if (Auth::user()->can('index',App\Model1::class) && Auth::user()->can('index',App\Model2::class))
#endif
You can find further information on Laravel's website at the link below:
https://laravel.com/docs/5.8/authorization#via-blade-templates
To take my answer further, it might be worth creating a separate class to handle authorization and then call it in the blade:
#if(BlogPermissions('showModal'))
#endif
you have to create policy for that. check the below code and for more reference check this https://laravel.com/docs/5.8/authorization#generating-policies
<?php
namespace App\Policies;
use App\User;
use App\Post;
class PostPolicy
{
/**
* Determine if the given post can be updated by the user.
*
* #param \App\User $user
* #param \App\Post $post
* #return bool
*/
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
}

PHP / Laravel - Combine result (Model relationship)

Right now I have below data:
Now the problem with above is, that it's creating a new array for each entry in my database.
I would very much like to get below output:
The difference is here that:
All the routes that are the same, should be grouped. So if the Route is the same, it should be under the same route list.
If the Carrier under the route is the same, then it should be under that route and carrier
If the route is different, it should start a new list (**Route: XYZ to XYZ)
Current Code:
Controller.php:
public function show()
{
$routes = Routes::where('id', '>=', 1)
->with('routeStops', 'getVessel')
->get()
->toArray();
foreach ($routes as $key => $value) {
echo '<h2>Route: ' . $value['origin'] . ' to ' . $value['destination'] . '</h2>';
$carrier = Carriers::where('id', $value['get_vessel']['carriers_id'])->first()->name;
echo 'Carrier: ' . $carrier . '';
dump($value);
}
}
This is my models:
Routes.php
/**
* Get the route stop associated with this route.
*/
public function routeStops()
{
return $this->hasMany('App\RouteStops', 'route_id');
}
/**
* Get the vessel record associated with this route.
*/
public function getVessel()
{
return $this->belongsTo('App\Vessels', 'vessel_id');
}
How can I do so I achieve the 2nd desired output?

Categories