I have users, app_roles, app_permissions, app_permission_app_role, app_role_user.
The tables are self explanatory, I am creating permissions, Then assigning that permissions to new role on role creation, And then i assigns roles to users.
i check permission of the authenticated user like :
#if(auth()->user()->can('some route name'))
Html...
#endif
The above condition checks if the user have access to that content or not based of the assigned role as we know that the role have permissions, And the can('some route name') parameter is a route name. Its working fine.
Where i am stuck !!!
The table app_role_user had user_id, app_role_id, Now i added another column organization_id... (Consider Organizations as groups, Where a user can be a member of that groups, And the owner of the group assigns single role(Can't assign multiple role) to that user). Because now the user can switch between organization and the user can have different roles in different organizations.
I have to clear path for the :
#if(auth()->user()->can('some route name'))
Html...
#endif
Note : : Auth::user()->current_org->id show the id of the organization in which the user is in right now
As well as currently i am saving role_id, user_id, organization_id in app_role_user table.
Here is my AuthServiceProvider class,
I am Dynamically registering permissions with Laravel's Gate :
public function boot(GateContract $gate)
{
$this->registerPolicies();
$this->registerAllPermissions($gate);
}
protected function getPermissions() {
return $this->app->make('App\Repositories\PermissionRepository')->withRoles();
}
private function registerAllPermissions($gate) {
if (Schema::hasTable('app_permissions') and Schema::hasTable('users') and Schema::hasTable('app_roles')) {
cache()->forget('app_permissions_with_roles');
foreach ($this->getPermissions() as $permission) {
$gate->define($permission->name, function ($user) use ($permission) {
return $user->hasPermission($permission);
});
}
}
}
Here is PermissionRepository class :
class PermissionRepository
{
protected $model;
public function __construct(AppPermission $model)
{
$this->model = $model;
}
public function all(){
return $this->model->all();
}
public function withRoles(){
$model = $this->model;
$permissions = cache()->remember('app_permissions_with_roles', 1*60*24, function() use($model) {
return $model->with('roles')->get();
});
return $permissions;
}
}
And here is HasRoles trait having hasPermission(AppPermission $permission) because AuthServiceProvider class needs it in registerAllPermissions.
trait HasRoles {
public function assignRole($role)
{
return $this->roles()->save(
AppRole::whereName($role)->firstOrFail()
);
}
public function hasRole($role)
{
if (is_string($role)) {
return $this->roles->contains('name', $role);
}
return !! $role->intersect($this->roles)->count();
}
public function hasPermission(AppPermission $permission)
{
return $this->hasRole($permission->roles);
}
}
What should i do, I have tried many conditions but nothing worked at all.
Looking forward to hear from you guys.
Thanks for the read, Need serious attention please.
You can try like this
User Model
//add organization_id as pivot field
public function roles(){
return $this->belongsToMany(AppRole::class)->withPivot('organization_id');
}
//define a function
public function orgRoles($orgId){
return $this->roles()->wherePivot('organization_id', $orgId)->get();
}
Now in trait modify hasRole function
public function hasRole($role)
{
$orgId = Auth::user()->current_org->id;
if (is_string($role)) {
return $this->orgRoles($orgId)->contains('name', $role);
}
return !! $role->intersect($this->orgRoles($orgId))->count();
}
Related
My DB schema looks like this.
Now, in artisan tinker mode, When I try to query Details table from user Model, it shows me the records of the details table but I cannot access the the Cases Model for some reason, it always returns NULL in tinker.
This is my User Model
public function details()
{
return $this->hasManyThrough('App\Models\Detail', 'App\Models\Cases', 'user_id', 'case_id', 'id', 'id');
}
What am I doing wrong?
If for convenience you want to access Details directly from the User model then you can define relations as - (may seem like a little duplication but worth if it results in ease)
class User extends Model
{
public function cases()
{
return $this->hasMany(Case::class);
}
public function details()
{
return $this->hasManyThrough(Detail::class, Case::class);
}
}
class Case extends Model
{
public function details()
{
return $this->hasMany(Detail::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
}
class Detail extends Model
{
public function case()
{
return $this->belongsTo(Case::class);
}
}
Now both cases and details can be directly accessed via User record
$user->cases;
$user->details;
The idea of hasManyThrough is to skip the intermediate table. If you need to look at the cases and the details maybe you should define other relations for it.
// User model
public function cases()
{
return $this->hasMany(Cases::class, 'user_id');
}
// Cases model
public function details()
{
return $this->hasMany(Detail::class, 'user_id');
}
$users = User::with('cases.details')->get();
foreach ($users as $user) {
// an user
foreach ($user->cases as case) {
// a case
foreach ($case->details as $detail) {
// the details of a case
}
}
}
I have 3 tables:
users
id
role
email
typable_id
typable_type
buyers
id
name
address
avatar
email
residential_id
residentials
id
name
city
state
And here is my model that shows the relationship
User.php
public function typable()
{
return $this->morphTo();
}
Buyer.php
public function residential()
{
return $this->belongsTo(Residential::class);
}
public function user()
{
return $this->morphMany(User::class, 'typable');
}
Residential.php
public function buyer()
{
return $this->hasMany(Buyer::class);
}
If I want to delete the residential, all buyers from that residential need to be deleted. Same as users need to be deleted too when the buyers is deleted. How can I do that? This is what insides my Residential Controller for destroy function.
ResidentialController
public function destroy(Request $request)
{
$residentials = Residential::find($request->input('id'));
$residentials->id = $request->input('id');
$residentials->name = $request->input('name');
$residentials->delete($residentials);
return response()->json($residentials);
}
I have tried to put this code to delete the buyers (for users not yet) inside destroy() but nothing is changed for the buyers to be deleted.
$buyers = Buyer::where('residential_id','=',$request->residential_id)->first(); $buyers->delete($buyers);
While this is the code that I managed to do if I want to delete the buyers, the users are deleted too.
BuyerController
public function destroy(Request $request)
{
$users = User::where('email', '=', $request->email)->first();
$buyers = Buyer::find($request->input('id'));
$buyers->id = $request->input('id');
$buyers->name = $request->input('name');
$buyers->delete($buyers);
$users->delete($users);
return response()->json($buyers);
}
I hope there is someone to help and teach me the correct way.
Approach-1
you can override the delete function for any model.
//Residential.php
public function delete()
{
$this->buyer->delete();
return parent::delete();
}
//Buyer.php
public function delete()
{
$this->user->delete();
return parent::delete();
}
Now when you delete any Residential record, the chain will first delete any related user and then delete buyer and finally delete the Residential record.
Approach-2
You can use each() method to get all relating buyer and then get all relating user.
$residentials->buyer
->each(function ($b) {
$b->user->each(function ($u) {
$u->delete();
});
$b->delete();
});
$residentials->delete();
You might want to register model events to handle that:
class Residential extends Model
{
// Lets use plural form for a HasMany relationship.
public function buyers()
{
return $this->hasMany(Buyer::class);
}
protected static function booted()
{
static::deleting(function ($user) {
// I am using Higher Order Message, check this out: https://laravel.com/docs/8.x/collections#higher-order-messages
$this->buyers->each->delete();
});
}
}
class Buyer extends Model
{
// Lets use the plural form for a MorpMany relationship.
public function users()
{
return $this->morphMany(User::class, 'typable');
}
protected static function booted()
{
static::deleting(function ($user) {
$this->users->each->delete();
});
}
}
And you only have to remove a single object in your controller:
class ResidentialController
{
public function destroy(Request $request)
{
$residential = Residential::findOrFail($request->input('id'));
$residential->delete();
// The framework is gonna automatically convert this to a JSON object.
return $residential;
}
}
class BuyerController
{
public function destroy(Request $request)
{
$buyer = Buyer::findOrFail($request->input('id'));
$buyer->delete();
// The framework is gonna automatically convert this to a JSON object.
return $buyer;
}
}
I have 3 models:
User
Role
Permission
Note:
a user can have direct permissions
a role can have permissions
a user can have roles
I'm trying to get the total permissions a user has, either through their direct permissions or through their roles permissions. So I need to combine both into 1 collection and count the total.
I've set up the belongsToMany relationships for User and Role:
public function permissions()
{
return $this->belongsToMany('App\Permission');
}
How do I do this?
You need to use the hasManyThrough relation
Here is the link to the documentation: Eloquent Documentation
so you would do something like this:
public function permissions()
{
$directPermissions = $this->belongsToMany('App\Permission');
$rolePermissions = $this->hasManyThrough('App\Permissions', 'App\Role');
return $directPermissions->merge($rolePermissions);
}
I figured it out via flatMap.
i am not sure but it will help
open your user model
public function roles()
{
return $this->belongsToMany(Role::class);
}
public function permissions()
{
return $this->belongsToMany(Permission::class);
}
public function hasRole(...$roles)
{
foreach($roles as $role)
{
if($this->roles->contains('name',$role))
{
return true;
}
}
return false;
}
public function hasPermission($permission)
{
return $this->hasPermissionThroughRole($permission) || (bool) $this->permissions->where('name',$permission->name)->count();
}
public function hasPermissionThroughRole($permission)
{
foreach($permission->roles as $role)
{
if($this->roles->contains($role))
{
return true;
}
}
return false;
}
Then open your Role Model and add these
public function permissions()
{
return $this->belongsToMany(Permission::class);
}
public function users()
{
return $this->belongsToMany(User::class);
}
and open your permission model
public function roles()
{
return $this->belongsToMany(Role::class);
}
public function users()
{
return $this->belongsToMany(User::class);
}
and finaly to boot all the permission to roles and user
run the command php artisan make:provider PermissionServiceProvider
open you service provider created newely and
add
use App\Permission;
use Illuminate\Support\Facades\Gate;
add the code under the boot method
Permission::get()->map(function ($permission)
{
Gate::define($permission->name, function ($user) use ($permission)
{
return $user->hasPermission($permission);
});
});
hope it helps if you find any difficulties please comment below
I'm making a blog with laravel. When I look into user authentication, I have a few issues here. I have 2 tables, one is users: id, name, ... the other is role: user_id, privilege .. I need to check whether a user is admin or not, I will need a function like isAdmin() or a $isAdmin attribute. This is my function placed in the app/providers/AuthServiceProvider.php:
private static $isAdmin;
public static function isAdmin() {
if (isset(self::$isAdmin)) {
return self::$isAdmin;
}
$user_privilege = DB::table('role')
->select('privilege')
->where([
['privilege', '=', 'admin'],
['user_id', '=', Auth::user()->id],
])
->get()
->first();
self::$isAdmin = isset($user_privilege->privilege);
return self::$isAdmin;
}
This code works fine, but this will require two queries to the database to check the user's admin rights. So I wanted to find a way to inject a query into Auth :: user () so that only one query would retrieve all the stuff I wanted. I'm a beginner with laravel. Can you help me?
I assume that user can have only one role. You can create isAdmin() method in the User model:
public function isAdmin()
{
return auth()->user()->role->privilege === 'admin';
}
Define the relationship if you didn't do that yet:
public function role()
{
return $this->hasOne(Role::class);
}
Then use it with auth()->user()->isAdmin().
If a user can have many roles:
public function isAdmin()
{
auth()->user()->loadMissing('roles');
return auth()->user()->roles->contains('admin');
}
And the relationship:
public function roles()
{
return $this->hasMany(Role::class);
}
On your User model define an isAdmin method:
public function isAdmin() {
// simplified your query here
return $this->hasRole('admin');
}
Then it will be accessible on the Auth guard like:
Auth::user()->isAdmin();
I am trying to make laravel basic authorization. I'm using gate for laravel authorization.
Table structure
User Table, Permission Table, Role, role_permission table
user : id, name , password, email
permission : id, name
role:id , name
role_permission: id, role_id, permission_id
authServiceProvider
public function boot(GateContract $gate)
{
$this->registerPolicies();
foreach($this->getPermissions() as $permission)
{
$gate->define($permission->name,function($user) use($permission){
return $user->role->id == $permission->role_id;
});
}
}
public function getPermissions()
{
$permissions = \DB::table('role_permission')
->join('permissions', 'permissions.id', '=', 'role_permission.permission_id')
->select('role_permission.*','permissions.*')
->get();
return $permissions;
}
It doesn't work properly means I can't access the route though it's there in permission table with the appropriate user.
You should not access database from service provider. Always try to keep your service provider simple. Please follow the bellow steps to serve your purpose.
AuthServiceProvider.class
public function boot(GateContract $gate)
{
$this->registerPolicies($gate);
$gate->before(function($user, $ability) {
return $user->hasPermission($ability);
});
}
Now add the following methods in App\User model.
public function hasPermission($name)
{
$permission = Permission::where('name','=', $name)->first();
if(! $permission) {
return false;
}
return $this->hasRole($permission->roles);
}
public function hasRole($role)
{
if (is_string($role)) {
return $this->roles->contains('name', $role);
}
return (bool) $role->intersect($this->roles)->count();
}
Hope this will work to serve your purpose.