Laratrust : Users and Roles Usage - php

I'm a Laravel user. I am currently trying to use the Laratrust package for web admin purposes. For User and Role implementations I want one user to hold multiple roles and one role many users can have. In my opinion, the most suitable relationship is Many to Many Relationship, am I correct? Then, is the function I made right?
App\User;
public function roles()
{
return $this->belongsToMany('App\Role');
}
App\Role;
public function users()
{
return $this->belongsToMany('App\User');
}
Then how do I display the two interconnected data in one blade file? Thanks.

Define relations as users() and roles(). You can display all roles of the user with something like this:
#foreach ($user->roles as $role)
{{ $role->name }}
#endforeach

Related

Laravel 8 - relationship return null

I have one more relationship in the project like this it works but this is not working. I can't see any value on the screen. It return null. I'm not good with Laravel and relationships in database. I think my controller and routes are correct but my relationships are incorrent.
My User model contains the following relationship:
public function follows(){
return $this->hasMany('App\Models\Follow');
}
My Follow model contains the following relationship:
public function user(){
return $this->belongsTo('App\Models\User');
}
My blade is:
#foreach ($user->follows as $followr)
<h6><strong>{{$followr}}</strong></h6>
#endforeach
please refer laravel eloquent documentation here
you can pass foreign key and local key as 2nd and 3rd arguement in hasMany function and pass foreign key and owner key as 2nd and 3rd arguement in belongsTo function

Laravel 5.8 Eloquent querying

I know all the Eloquent model querying is in the Laravel documentation and I have read through them to figure this out but can't seem to work my way around it. I am building a recipe application, and I want to get access to a particular profile picture of a user based on an uploaded recipe.
I have a grid of recipes that have been uploaded by various users. The User and Recipe model are below.
User Model
public function recipes()
{
return $this->hasMany('App\Recipe');
}
Recipe Model
public function user()
{
return $this->belongsTo('App\User');
}
In my recipe grid, I have the following code in a recipe card.
Index Blade/View
#foreach($recipes as $recipe)
<img class="recipeProfilePic" src="/storage/profile_pictures/{{What goes here}}" alt=""><a
href="{{ route('user', $recipe->user_id) }}">
<small>{{$recipe->author}}</small>
</a>
#endforeach
Through the $recipe I can access the name of the creator of that particular recipe like this $recipe->author. I am trying to get access to this specific users profile photo too. I want to get something like this $recipe->author->profile_pic. (profile_pic is a column in my users table).
How would I go about solving this problem in a simple way? I'm a newcomer to coding and am still working on solving these problems myself. Thank you!
You can do it like this:
$recipe->user->profile_pic
You can change the model by rename user to author like this:
public function author()
{
return $this->belongsTo('App\User');
}
or changing view (blade) like this:
#foreach($recipes as $recipe)
<img class="recipeProfilePic" src="/storage/profile_pictures/{{What goes here}}" alt=""><a
href="{{ route('user', $recipe->user_id) }}">
<small>{{$recipe->user->profile_pic}}</small>
</a>
#endforeach

How to have Acces level Control the easiest way?

I am designing a CMS and i have setup users based on the role.How do i limit the users of their permissions based on their access level?
The easiest way is to get users by their role. Have a column for your users table called role or whatever you name it.
You can do Access Level Control easily with Gates
In your app\Providers\AuthServiceProvider register your policy. Example:
use Illuminate\Support\Facades\Gate;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
public function boot(GateContract $gate)
{
$this->registerPolicies($gate);
$gate->define('isUser', function($user){
return $user->role == 'user';
});
$gate->define('isDealer', function($user){
return $user->role == 'dealer';
});
}
isUser , isDealer are the user Types we are defining to Use in the project blade,controllers.You can change it as you like.Role is the column that you created in the table and we are comparing with the table values which are the user types user and dealer.
you can limit values in blade with laravel method
#can('isUser')
<only visible to users based on role user>
#endcan
It will be still accessible via routes so you can limit via controller functions or routes.
//controller
public function create()
{
if(!Gate::allows('isUser')){ // || for multiple parameters can('isAdmin' || 'isUser)
abort(404,"Abort");
}
return view('yourView');
}
This way the controller function will be not accessible for the roles defined.
Check the official documentation for in detail methods and information.

Laravel show data

I'm new with Laravel or any MVC.
I created the models, I create the route from web.php, I created the view with Blade, I created the controller and fetched all the data from a single model. Now, I want to make a join or something similar.
I want to get something this:
Name | Section name | Status.
In my user Model I have:
ID | Name | SectionID | Status
In my sections Model I have:
ID | SectionID | Active
Currently I created the controller like this:
public function show($name)
{
if(User::where('name', '=', $name)->exists())
{
return view('profile', ['user'=>User::where('name', $name)->first()]);
}
else
{
return 'User not found';
}
}
And in my Blade I'm getting content like this: {{ $user->name }}
Hmm you have to define the relationship between user and section model.
Please check this thread for how to work with relationships in laravel Laravel Eloquent Relationships.
Then whenever you get the user you can load any relationship that its defined on User model.
In your case you are performing two unecessary database calls, so I would suggest you to check out DRY principle.
Lets say in User has one Section you define the relationship in User model like so:
public function sections(){
return $this->hasOne(Section::class);
}
Then the reverse relationship from section model:
public function user(){
return $this->belongsTo(User::class);
}
So now your method in controller might look something along the lines of this:
public function show($name){
return view('profile', ['user'=>User::with('sections')->where('name', $name)->firstOrFail()]);
}
This way you eagerload the sections that are tied to that user, for more about Laravel eagerloading
Then in the blade you can access any of the attributes from user or section model.

Laravel 5.1 users, roles and actions

I am creating an application using Laravel 5.1 with users, roles and actions.
The table setup is like so:
user
id name
1 John Smith
2 Fred Smith
role
id name
1 Administrator
2 Customer
role_user
user_id role_id
1 1
2 1
action
id name description path
1 dashboard ability to access dashboard /admin
2 editAdmin ability to edit admins /admin/users/administrators/{id}/edit
action_role
action_id role_id
1 1
2 1
The user table holds ALL users on the site, including administrators and customers.
The role table holds all the possible roles a user can have. For example Administrator or Customer.
The role_user table is a pivot table which links role to user.
The action table lists all of the actions possible (i.e. urls or routes) on the app.
The action_role is a pivot table which links action to role.
So to summarise:
Users have roles
Roles have actions
A user can have many roles
A role can have many actions
I want to have a middleware setup which checks on page load if the user has permissions to view the current page. In order to do this, I need to be able to access a users actions, using a call like this:
$user->roles()->actions();
How do I setup my eloquent relationships to support this kind of call?
UPDATE
My relationships are setup like so in my models:
User
/**
* The roles that belong to the user.
*
* #return Object
*/
public function roles()
{
return $this->belongsToMany('App\Models\User\Role')->withTimestamps();
}
Role
/**
* The actions that belong to the role.
*
* #return Object
*/
public function actions()
{
return $this->belongsToMany('App\Models\User\Action');
}
/**
* The users that belong to the role.
*
* #return Object
*/
public function users()
{
return $this->belongsToMany('App\Models\User\User');
}
Action
/**
* The roles that belong to the action.
*
* #return Object
*/
public function roles()
{
return $this->belongsToMany('App\Models\User\Role');
}
Eloquent does not have a HasManyThrough relationship across 2 pivot tables.
1. You can get the actions by lazy eager loading the roles and actions, then extracting them:
$actions = $user->load('roles.actions')->roles->pluck('actions')->collapse()->unique();
2. You can check the action directly in the database, using a whereHas constraint:
$allowed = $user->roles()->whereHas('actions', function ($query) use ($action) {
$query->where('name', $action);
})->exists();
For handling user, user roles and user permissions you can simply use Toddish package.
There are lot of things this package does for you. like:
$user->is('Your Defined Role');
$user->can('add_user');
$user->level(7);
For installation just read it's documentation.
Although you have mentioned that your Eloquent models (and their relationships) are set up, I am assuming that you have the following in your application already:
User model
class User extends Eloquent {
public function roles() {
return $this->belongsToMany('App\Models\Role');
}
}
Role model
class Role extends Eloquent {
public function actions() {
return $this->belongsToMany('App\Models\Action');
}
public function users() {
return $this->belongsToMany('App\Models\User');
}
}
Action model
class Action extends Eloquent{
public function roles(){
return $this->belongsToMany('App\Models\Role');
}
}
Given the above set properly, you can't certainly make a call like the following as Laravel will assume you're making a call to a Query Builder method or whatever [which in fact doesn't exist]:
$userActions = App\Models\User::find(1)->roles()->actions();
Instead, you have to use Laravel's magical whereHas method to query relations where multiple nested relations are involved.
Now, having the user's id and the current allowed action [which indeed can be utilized in your middleware], it can be determined whether the user is allowed to see the page:
$hasAccess = App\Models\User::whereHas('roles.actions', function($q) use ($id, $action){
$q->where('users.id', $id);
$q->where('actions.name', $action);
})->get()->toArray();
if($hasAccess){ // or `count($hasAccess) > 0`. The former will work since any number > 0 evaluates to true in PHP
//user has access
}
Notice the nesting of relationships with ..
Relationship Existence in Laravel:
In Laravel the existence of a relationship between models is determined with has and whereHas methods. The has method is used to only determine if a relationship exists, e.g. by executing App\User::has('roles')->get() you'll always get a list/collection of users which at least have any roles.
More power to this can be added with whereHas with which you can add where clauses to the actual query.
Hi this is how I solved this.
I didn't use an actions table. Just users, roles and the user_role table.
After the solution, I pass my custom middleware a role array to every route I create like:
Route::get('insights',[
'as'=>'insights',
**'middleware'=>['access.level'],**
**'roles'=>['admin','customer'],**
'uses'=>'CustomersController#index'
]);
Files modified:
app\Http\Controllers\Controller.php
Custom middleware: CheckAccessLevel
app\Http\Kernel.php
app\Http\routes.php
Controller.php
In this file, I wanted to eager load the current logged in user with his roles and make it a global variable in all views as {{currentUser}} and in all my controllers as "$this->currentUser". this is the code:
protected $currentUser;
public function __construct() {
if(isset(Auth::user()->username)){
$this->currentUser = User::with('roles')->where(['username' => Auth::user()->username])->first();
// Share this property with all the views and controllers in your application.
view()->share('currentUser', $this->currentUser);
}
}
Custom Middleware: CheckAccessLevel.php
Over here in the middleware, I retrieve the roles array passed to the route and and also the roles assigned to the current user.
After i get these variables, I intersect them to see if there is a match before I pass the user on. check the code below:
//if user is not logged in, show them where to login :)
if (!Auth::check()) {
return redirect()->route('user::login');
}
//Otherwise get the roles for that route
$get_route_action = $request->route()->getAction();
$get_route_roles = $get_route_action['roles'];
//Eager load the user with their list of roles
$eager_user = User::with('roles')->where(['id'=>$request->user()->id])->first();
$user_roles = $eager_user->roles->pluck('role_name')->toArray();
//intersect the users roles with the route roles to see if there is a match
foreach ($user_roles as $user_role){
if(in_array($user_role, $get_route_roles)){
return $next($request);
}
}
Kernel.php
over here, i register my route as "access.level" inside the route middleware group
Route.php
then whenever I create a route, I just pass in the allowed role names to it and voala! It works for me..
I hope this helps.
You need a HasManyThrough relationship here.. Here's something to get you started.
Judging by your given tables I see it has a one-to-many relationship with user and roles. While action and roles also has many-to-many relationship.
But first you need to create models for each entities(user,role,action) to follow the MVC structure. You could easily make these models from the command line using the Laravel's artisan with this command.
php artisan make:model User
Make sure to also add or change columns in the newly created migrations. Assuming you also have your pivot tables set up for the 3 tables, you can now add the relationships.
class User extends Model {
//
protected $table = 'users';
/*
* #var table
*
*/
public function action()
{
return $this->hasManyThrough('App\Action', 'App\Role');
}
}
Now you have a HasManyThrough relationship. you can query like this directly no need to include role.
//this
$actions = User::find(1)->action();
//instead of
$actions = User::find(1)->role()->action();
//this will return all actions available to a user with id of 1
NOTE I also suggest you make your role and user models to be on a one to one relationship.

Categories