There is a multi-tenant laravel application that is implemented in a single database. There are models User and Role with the following relation in User model:
public function roles(): BelongsToMany
{
$relation = $this->belongsToMany(Roles::class, 'role_users', 'user_id', 'role_id')->withTimestamps()->withPivot('tenant_id');
$relation = $relation->where(function ($query) {
$query->where('tenant_id', Tenant::current()->id);
});
return $relation;
}
The role_users table contains user_id, role_id, tenant_id. A user may present in multiple tenants, when I update roles of a user in a tenant, I need to only update the roles of the current tenant, not the user roles on all tenants. This the code I write :
$user->roles()->syncWithPivotValues($roles, ['tenant_id' => Tenant::current()->id]);
But it doesn't work and syncs all roles not the roles of the current tenant. How can I solve the problem?
You can use the wherePivot() method passing the tenant id.
You can find the documentation here: https://laravel.com/docs/9.x/eloquent-relationships#filtering-queries-via-intermediate-table-columns
The code should then look like this:
$user->roles()->wherePivot('tenant_id', Tenant::current()->id)->sync($roles);
Related
I have one user table named "Users".
There are different roles (trainer and client).
I want to make like Trainer can have many clients, but client can have one trainer.
I made table in db like "ClientTrainer" where is stored only ID of users with columns
"id, client_id, trainer_id"
and I am trying to return something like this:
$user->allclients(); - return all clients (logged as trainer)
$user->owntrainer(); - return his trainer (logged as client)
Its fresh laravel 5.7 with only Auth and Roles.
I am sure it can only be done with modals, but i need a little help.
Thanks.
First, create a model for table ClientTrainer as well. Then try this inside your Users model:
public function clients()
{
return $this->belongsToMany(
"UsersModelNamespace",
"IntermediaryTableModelNamespace",
"trainer_id",
"client_id",
"id",
"id"
);
}
public function trainer() // will return a collection with one element
{
return $this->belongsToMany(
"UsersModelNamespace",
"IntermediaryTableModelNamespace",
"client_id",
"trainer_id",
"id",
"id"
);
}
Why I'm posting belongsToMany when retrieving the trainer? Laravel 5.7 does not support hasOneThrough() relationship.
The best thing you can do is to separate your Table into two; "users" & "trainers".
Even if they have exactly the same fields, they dont have the same functionality, so that justifies the separation. Then just add trainer_id in your table "users"
I have two models, user and permissions. 1 user can have many permissions, a user_id will have multiple occurrences on the permissions table. When updating an user I may change its permissions.
users permissions
id id
name user_id
With eloquent I do something like this:
$user = User::find(1);
$user->permission()->delete();
$user->permission()->create($permissions);
Although this code is very logical, it doesn't seem right to me. Finding the user, scanning the table to remove its permissions and reinserting new ones seems amateur, the deletion part doesn't seem right to me. To achieve one task I have to go through multiple processes.
If at least was something like this:
$user = User::find(1);
$user->permission()->recreate($permissions);
Of course recreate would have to be a built-in function and I am willing to code that functionality. Can't say if am doing things right.
Are you sure you are doing it rigth?
Since a user can have many permissions, you should have a user, permissions and a user_has_permissions or something like useR_permissions table.
users permissions user_has_permissions
id id user_id
name name permission_id
... ...
This is the relationships:
User permissions (in user model):
public function permissions()
{
return $this->belongsToMany(Permission::class, 'user_has_permissions');
}
To check if the user has permissions (in user model):
public function hasPermission($permissionId)
{
return null !== $this->permissions()->where('permission_id', $permissionId)->first();
}
You can also have a roles table, that has many permissions:
users permissions user_has_permissions roles role_has_permissions user_has_role
id id user_id id role_id user_id
name name permission_id name permission_id role_id
... ... ...
Now, user can have permissions and permissions through roles.
The user roles (in user model):
public function roles()
{
return $this->belongsToMany(Role::class, 'user_has_roles');
}
The role permissions (in the Role model):
public function permissions()
{
return $this->belongsToMany(Permission::class, 'role_has_permissions');
}
To check if the user has permissions through roles (in the user model):
public function hasPermissionThroughRoles($permissionId)
{
$roles = $this->roles;
foreach($roles as $role){
if($this->roles->contains($permissionId)
return true;
}
}
You can modify the hasPermissios function to check if the user has permission through roles, and if yes, grant access:
public function hasPermission($permissionId)
{
return (bool)($this->permissions()->where('permission_id', $permissionId)->first() || $this->hasPermissionThroughRoles($permissionId));
}
From laravel docs:
Eloquent also provides a few additional helper methods to make working with related models more convenient. For example, let's imagine a user can have many roles and a role can have many users. To attach a role to a user by inserting a record in the intermediate table that joins the models, use the attach method.
With the provided code snippets, you can add permission or role to the user with
$user = User::find(1);
$user->permissions()->attach(1);
$user->roles()->attach(1);
or remove permissions/roles:
$user = User::find(1);
$user->permissions()->detach(1);
$user->roles()->detach(1);
Check this package. The user permissions and roles (or group, as it is called in the package) management is easy with it.
Hope it helps.
I have tables:
user
id
name
companies
id
name
company_user
company_id
user_id
Tables has Many To Many relationships.
As it complicated relationship for me, I can't find way how to make this limit, when user can see companies that was created by this user. (probably not well experienced)
Now I have this, but user can see any company
CompanyController:
public function show($company_id)
{
$company = Company::where('id', $company_id)->firstOrFail();
return view('company.settings', compact('company'));
}
So tip me please how to make user can see only companies created by this user.
You can do this:
public function show($company_id)
{
$company = auth()->user()->companies()->findOrFail($company_id);
return view('company.settings', compact('company'));
}
It will scope the company to the currently logged in user (through the many-to-many relationship on the User model). If none is found, it will return 404.
Since it many to many relation, you can map one company to many users, also map one user to many companies. Check if you have not mistakenly assign the company to many user
Also the above code can be written the way
public function show($company_id)
{
$company = Company::findOrFail($company_id);
return view('company.settings', compact('company'));
}
I create all model and relations .
one user have many Roles and one Role have many Permissions.
i want a elequent that give a user with all roles and all permission for per role.
now I create one function for this :
public function getUserByMobileNumberWithRolesAndPermissions($mobile_number)
{
$users = user::with('roles.permissions')->whereMobileNumber($mobile_number)->first();
return is_null($users) ? null : $users->toArray();
}
In Laravel we can manage Users and Permissions easly but i've a problem with my application.
In my application a User is attached to One or Many department.
But a User can have different Role/Permission between departments. That is the problem. In the department One he can have a Admin Role and in the department Two he can only have a User Role. When the User switch between department i would like that his Role can be update.
How i can manage this in Laravel and Eloquent ?
Thank you for your help.
Jeffrey
Without seeing any of your code, I am forced to be fairly generic here. But here is the basic concept.
Architecture
Assuming you have tables like departments, users, roles, and permissions already, all you would need next is define a joining table. Something like this:
department_role_user
department_id // for this department
role_id // this role is assigned to
user_id // this user
Authorization
Define something like a hasPermissionTo() method on your User model.
Definition
class User
{
public function hasPermissionTo($action, $department)
{
// first get permission
$permission = Permission::where('action', $action)->first();
// get all roles which have this permission
$roles = $permission->roles;
return DB::table('department_role_user')
->where('user_id', $this->id) // current user
->where('department_id', $department->id) // the dept
->whereIn('role_id', $roles->pluck('id')) // any of the roles
->exists();
}
}
Usage
And use it like so.
if ($user->hasPermissionTo('do-something', $someDept)) {
// allow action
} else {
// no way
}
This should also work nicely with Laravel's Gates and Policies. Just use your new hasPermissionTo() method inside your gate/policy definitions.