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
Related
In my app i have define relationship (profile, user, level) but when I fetch data it is showing an error (Trying to get property 'email' of non-object) how can i solve this thank in advance.
this is User Model
public function profile()
{
return $this->hasOne(Profile::class, 'user_id');
}
Profile Model
public function user()
{
return $this->belongsTo(User::class, 'id');
}
public function level()
{
return $this->belongsTo(Level::class, 'id');
}
Level Model
public function profile()
{
return $this->hasOne(Profile::class, 'level_id');
}
This is Controller ProfileController
$users = Profile::with(['user', 'level'])->where('is_bd_partner', 'Yes')->get();
foreach ($users as $key => $value)
{
echo $value->first_name.'<br>';
echo $value->last_name.'<br>';
echo $value->user->email.'<br>';
echo $value->level->level.'<br>';
}
Note that belongsTo takes foreign_key as the first parameter.
So you should change the Profile Model as
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}
public function level()
{
return $this->belongsTo(Level::class, 'level_id');
}
Read more here
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();
}
I have 3 models with the relations many-to-many:
Module
public function permissionTypes()
{
return $this->belongsToMany(PermissionType::class, 'permissions')->withPivot('role_id');
}
public function roles()
{
return $this->belongsToMany(Role::class, 'permissions')->withPivot('permission_type_id');
}
Role
public function permissionTypes()
{
return $this->belongsToMany(PermissionType::class, 'permissions')->withPivot('module_id');
}
public function modules()
{
return $this->belongsToMany(Module::class, 'permissions')->withPivot('permission_type_id');
}
PermissionType
public function roles()
{
return $this->belongsToMany(Role::class, 'permissions')->withPivot('module_id');
}
public function modules()
{
return $this->belongsToMany(Module::class, 'permissions')->withPivot('role_id');
}
tables description:
modules
id
title
status
roles
id
title
permission_types
id
title
pivot table permissions
id
role_id
module_id
permission_type_id
My synchronization looks like:
//array of ids from request to synchronization
$permissions = $request['permissions'];
//role by id from request
$role = Role::findOrFail((int)$roleId);
//module by id from request
$module = Module::findOrFail((int)$moduleId);
//synchronization
$pivotData = array_fill(0, count($permissions), ['role_id' => $role->id]);
$syncData = array_combine($permissions, $pivotData);
$module->permissionTypes()->sync($syncData);
When trying to make the synchronization, have an error
QueryException in Connection.php line 647:
SQLSTATE[42000]: Syntax error or access violation: 1066 Not unique table/alias: 'permissions' (SQL: select permissions.*, permissions.role_id as pivot_role_id, permissions.permission_id as pivot_permission_id from permissions inner join permissions on permissions.id = permissions.permission_id where permissions.role_id = 1)
Thanks
IMHO you are trying to design a triple many to many that does not exists in Laravel. The solution, usually is to give the pivot table (in your case permissions) a Model (Permission in below code) for defining hasManyThrough relations.
If I understood well your table structure I will design the following relationships:
Module
public function permissions()
{
return $this->hasMany(Permission::class);
}
public function roles()
{
return $this->hasManyThrough(Role::class, Permission::class);
}
public function permissionTypes()
{
return $this->hasManyThrough(PermissionType::class, Permission::class);
}
Role
public function permissions()
{
return $this->hasMany(Permission::class);
}
public function permissionTypes()
{
return $this->hasManyThrough(PermissionType::class, Permission::class);
}
public function modules()
{
return $this->hasManyThrough(Module::class, Permission::class);
}
PermissionType
public function permissions()
{
return $this->hasMany(Permission::class);
}
public function modules()
{
return $this->hasManyThrough(Module::class, Permission::class);
}
public function roles()
{
return $this->hasManyThrough(Role::class, Permission::class);
}
Permission
public function permissionType()
{
return $this->belongsTo(PermissionType::class);
}
public function role()
{
return $this->belongsTo(Role::class);
}
public function module()
{
return $this->belongsTo(Module::class);
}
Tell me if it could work for you.
I am trying to access the middle table attributes of many to many relationships using pivot but it return nulls.
class User extends Modal
{
public function packages()
{
return $this->belongsToMany('App\Package');
}
}
Class Package extend Model
{
public function users()
{
return $this->belongsToMany('App\User');
}
}
$package->pivot->created_at
but it returns null.
although i have a package associated to user.
By default, only the model keys will be present on the pivot object. If your pivot table contains extra attributes, you must specify them when defining the relationship:
public function packages()
{
return $this->belongsToMany('App\Package')->withPivot('created_at');
}
public function users()
{
return $this->belongsToMany('App\User')->withPivot('created_at');
}
Docs
Try this one:
class User extends Modal
{
public function packages()
{
return $this->belongsToMany('App\Package')->withTimestamps();
}
}
Class Package extend Model
{
public function users()
{
return $this->belongsToMany('App\User')->withTimestamps();
}
}
Make sure you have timestamps in your table.
Schema::table('user_package', function (Blueprint $table) {
$table->timestamps();
});
you can do this by adding in your migrations
class User extends Modal
{
public function packages()
{
return $this->belongsToMany('App\Package')->withTimestamps();
}
}
Class Package extend Model
{
public function users()
{
return $this->belongsToMany('App\User')->withTimestamps();
}
}
if you dont add this line your timestamps will not be saved in database.
return $this->belongsToMany('App\User')->withTimestamps();
Hope this helps.
class Admin {
public function user()
{
return $this->morphOne('App\Models\User', 'humanable');
}
public function master()
{
return $this->hasOne('App\Models\Master');
}
}
class Master {
public function admin()
{
return $this->hasOne('App\Models\Admin');
}
}
class User {
public function humanable()
{
return $this->morphTo();
}
public function images()
{
return $this->hasOne('\App\Models\Image');
}
}
class Image {
public function user()
{
return $this->belongsTo('\App\Models\User');
}
}
Now if I dump this:
return \App\Models\Admin::where('id',1)->with(array('user.images','master'))->first();
I get the perfect result one master, one user and one image record.
But if I do this
return $user = \App\Models\User::where('id',1)->with(array('humanable','humanable.master'))->first();
I only get one Admin record, the query get * from masters doesn't even run.
Any idea what I'm doing wrong, I'm sure this is possible.
If I remember correctly Laravel has lots of pitfall. You can try to use the protected $with var in Admin model instead of query builder with function.
class Admin {
protected $with = ['master'];
public function user() {
return $this->morphOne('App\Models\User', 'humanable');
}
public function master() {
return $this->hasOne('App\Models\Master');
}
}
In query builder, only need to include humanable. Now you should see master inside the humanable object.
return $user = \App\Models\User::where('id',1)->with('humanable')->first();
Hope this help.