Add roles / permissions per team for user

Hello, I have a case where I use spatie permissions, and I want to be able to give user roles and permissions on a team, can someone help with with the set of relations I have to do? I have: User - model
php public function teams(): BelongsToMany
{
return $this->belongsToMany(Team::class, 'team_user')->using(TeamUser::class);
}
php public function teams(): BelongsToMany
{
return $this->belongsToMany(Team::class, 'team_user')->using(TeamUser::class);
}
Team - Model
public function users()
{
return $this->hasMany(TeamUser::class);
}
public function users()
{
return $this->hasMany(TeamUser::class);
}
TeamUser - Model
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}

public function team()
{
return $this->belongsTo(Team::class, 'team_id');
}
public function user()
{
return $this->belongsTo(User::class, 'user_id');
}

public function team()
{
return $this->belongsTo(Team::class, 'team_id');
}
public function roles(): BelongsToMany
{
$relation = $this->morphToMany(
config('permission.models.role'),
'model',
config('permission.table_names.model_has_roles'),
config('permission.column_names.model_morph_key'),
app(PermissionRegistrar::class)->pivotRole
);


if (! app(PermissionRegistrar::class)->teams) {
return $relation;
}

$teamsKey = app(PermissionRegistrar::class)->teamsKey;
$relation->withPivot($teamsKey);
$teamField = config('permission.table_names.roles').'.'.$teamsKey;


return $relation->wherePivot($teamsKey, $this->team_id);
//->where(fn ($q) => $q->whereNull($teamField)->orWhere($teamField, $this->team_id));
}
public function roles(): BelongsToMany
{
$relation = $this->morphToMany(
config('permission.models.role'),
'model',
config('permission.table_names.model_has_roles'),
config('permission.column_names.model_morph_key'),
app(PermissionRegistrar::class)->pivotRole
);


if (! app(PermissionRegistrar::class)->teams) {
return $relation;
}

$teamsKey = app(PermissionRegistrar::class)->teamsKey;
$relation->withPivot($teamsKey);
$teamField = config('permission.table_names.roles').'.'.$teamsKey;


return $relation->wherePivot($teamsKey, $this->team_id);
//->where(fn ($q) => $q->whereNull($teamField)->orWhere($teamField, $this->team_id));
}
But I get an error when I try to save the team, when I select user roles This is how I was thinking to make it using repeater, Any help would be really appriciated. Thanks
No description
26 Replies
Lara Zeus
Lara Zeus3mo ago
try to make the column team_id nullable in the db and see if that fix it for more: https://filamentphp.com/docs/3.x/forms/advanced#saving-data-to-a-belongsto-relationship
jamesro
jamesroOP3mo ago
Do I also need to remove team_id from primary I suppose also right ?
No description
Lara Zeus
Lara Zeus3mo ago
yes, I add a new migration to do so but you can test and edit it directly on the db see if this solve the issue
jamesro
jamesroOP3mo ago
The sql error is solved but team_id field remains empty
jamesro
jamesroOP3mo ago
No description
Lara Zeus
Lara Zeus3mo ago
this from my user resource, so adjust the code as needed but this to give you an idea you can dd(getPermissionsTeamId()) to check if the team id is set correctly also you can use Filament::getTenant()->id
->saveRelationshipsUsing(function (Model $record, $state) {
$record->roles()->syncWithPivotValues(
$state,
['company_id' => getPermissionsTeamId() ?? null]
);
})
->saveRelationshipsUsing(function (Model $record, $state) {
$record->roles()->syncWithPivotValues(
$state,
['company_id' => getPermissionsTeamId() ?? null]
);
})
jamesro
jamesroOP3mo ago
@Lara Zeus i get no luck I get this error when trying to save SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1-52-App\Models\TeamUser' for key 'PRIMARY' (Connection: mysql, SQL: insert into model_has_roles (model_id, model_type, role_id) values (52, App\Models\TeamUser, 1)) this is my form
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('name')
->required()
->maxLength(255),
Forms\Components\TextInput::make('slug')
->required()
->maxLength(255),


Forms\Components\Repeater::make('users')
->label(function () {
return 'Members';
})
->columnSpan('2')
->relationship('users')
->schema([
Forms\Components\Grid::make(3) // Defines a grid with 4 columns
->schema([
Select::make('user_id')
->label('Users')
->relationship('user', 'nume')
->required(),
Select::make('roles')
->label('Roles')
->multiple()
->preload()
->relationship('roles', 'name')
])
])
->addActionLabel('Add member'),
]);
}
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('name')
->required()
->maxLength(255),
Forms\Components\TextInput::make('slug')
->required()
->maxLength(255),


Forms\Components\Repeater::make('users')
->label(function () {
return 'Members';
})
->columnSpan('2')
->relationship('users')
->schema([
Forms\Components\Grid::make(3) // Defines a grid with 4 columns
->schema([
Select::make('user_id')
->label('Users')
->relationship('user', 'nume')
->required(),
Select::make('roles')
->label('Roles')
->multiple()
->preload()
->relationship('roles', 'name')
])
])
->addActionLabel('Add member'),
]);
}
CodeWithDennis
CodeWithDennis3mo ago
As long as you have the right relations set up, like users being part of a company and a company having multiple users, you should be good.
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
// Multi-Tenancy
public function companies(): BelongsToMany
{
return $this->belongsToMany(Company::class);
}
// Multi-Tenancy
public function companies(): BelongsToMany
{
return $this->belongsToMany(Company::class);
}
I use ⁠ #bezhansalleh-shield with multi-tenancy works pretty good.
toeknee
toeknee3mo ago
Thye state multi-tenancy isn't supported...
CodeWithDennis
CodeWithDennis3mo ago
Yea mean spatie?
CodeWithDennis
CodeWithDennis3mo ago
Im using it on the "employee" panel, but on my "customer" panel it also works? It all depends on your policies, right?
jamesro
jamesroOP3mo ago
do you guys think is oky this relation I have in my User mode?
public function teams(): BelongsToMany
{
return $this->belongsToMany(Team::class, 'team_user')->using(TeamUser::class);
}
public function teams(): BelongsToMany
{
return $this->belongsToMany(Team::class, 'team_user')->using(TeamUser::class);
}
do I really need that TeamUser ? from what I see the problem starts here:
Forms\Components\Repeater::make('userss')
->label(function () {
return 'Members';
})
->columnSpan('2')
->relationship('users') // Assuming there's a relationship for products
->schema([
Forms\Components\Grid::make(3) // Defines a grid with 4 columns
->schema([
Select::make('user_id')
->label('Users')
->relationship('user', 'nume')
->required(),
Select::make('roles')
->label('Roles')
->multiple()
->preload()
->relationship('roles', 'name')
])
])
Forms\Components\Repeater::make('userss')
->label(function () {
return 'Members';
})
->columnSpan('2')
->relationship('users') // Assuming there's a relationship for products
->schema([
Forms\Components\Grid::make(3) // Defines a grid with 4 columns
->schema([
Select::make('user_id')
->label('Users')
->relationship('user', 'nume')
->required(),
Select::make('roles')
->label('Roles')
->multiple()
->preload()
->relationship('roles', 'name')
])
])
i saves me the roles in the model_has_roles table, but it doesn't insert the team_id value, which is actually the record->id that i'm currently editing..
CodeWithDennis
CodeWithDennis3mo ago
That makes sense, right? Since it's a pivot table, they just assign the role to the user, correct? Now you need to figure out if you can add another value to a column before saving it.
Lara Zeus
Lara Zeus3mo ago
saveRelationshipsUsing
jamesro
jamesroOP3mo ago
@Lara Zeus using saveRelationshipsUsing on the repeater, does nothing trying to place a dd right after
->saveRelationshipsUsing(function ($component, $set, $get, $record) {
dd('die');
})
->saveRelationshipsUsing(function ($component, $set, $get, $record) {
dd('die');
})
it goes by and inserts a value in model_has_roles with team_id missing
CodeWithDennis
CodeWithDennis3mo ago
GitHub
Mutate data before saving to a pivot table · filamentphp filament ·...
I have 3 tables: client, contact and client_contact, client_contact being a pivot table. Alongside the ids of client and contact it should save additional data like the current user's id. But I...
CodeWithDennis
CodeWithDennis3mo ago
Is this something that can help?
jamesro
jamesroOP3mo ago
array:1 [// app/Filament/Resources/TeamResource.php:73
"record-68" => array:6 [
"id" => 68
"team_id" => 1
"user_id" => 236
"created_at" => "2024-09-26T09:15:09.000000Z"
"updated_at" => "2024-09-26T09:15:09.000000Z"
"roles" => array:1 [
0 => "1"
]
]
]
array:1 [// app/Filament/Resources/TeamResource.php:73
"record-68" => array:6 [
"id" => 68
"team_id" => 1
"user_id" => 236
"created_at" => "2024-09-26T09:15:09.000000Z"
"updated_at" => "2024-09-26T09:15:09.000000Z"
"roles" => array:1 [
0 => "1"
]
]
]
managed to catch something when I did
->saveRelationshipsUsing(function ($component, $set, $get, $record) {
dd($get('userss'));
})
->saveRelationshipsUsing(function ($component, $set, $get, $record) {
dd($get('userss'));
})
i tried this also
->saveRelationshipsUsing(function ($component, $set, $get, $record) {
$record->load('users.roles');
$rolesArray = [];

foreach ($get('userss') as $userTeam) {
$rolesArray[$userTeam['id']] = $userTeam['roles'];
}

// Sync roles with team_id
$record->users()->each(function (TeamUser $teamUser) use ($rolesArray) {
$teamUser->roles()->syncWithPivotValues($rolesArray[$teamUser->id], ['team_id' => $teamUser->team_id, 'model_id' => $teamUser->id]);
});

})
->saveRelationshipsUsing(function ($component, $set, $get, $record) {
$record->load('users.roles');
$rolesArray = [];

foreach ($get('userss') as $userTeam) {
$rolesArray[$userTeam['id']] = $userTeam['roles'];
}

// Sync roles with team_id
$record->users()->each(function (TeamUser $teamUser) use ($rolesArray) {
$teamUser->roles()->syncWithPivotValues($rolesArray[$teamUser->id], ['team_id' => $teamUser->team_id, 'model_id' => $teamUser->id]);
});

})
but no joy, somehow the model_has_roles is getting the values inserted without team_id before even to reach my each statement and I get sql duplicat id error @CodeWithDennis @Lara Zeus any idea ? 😢
mohdaftab
mohdaftab3mo ago
have you tried setting up setPermissionTeamId($team_id) before setting roles?
jamesro
jamesroOP3mo ago
yeeye i think i sorted it, now it saves correctly my
->saveRelationshipsBeforeChildrenUsing(function ($component, $set, $get, $record) {
$record->load('users.roles');
$rolesArray = [];
foreach ($get('userss') as $userTeam) {

$rolesArray[$userTeam['id']] = $userTeam['roles'];
}

// Sync roles with team_id
$record->users()->each(function (TeamUser $teamUser) use ($rolesArray) {

$teamUser->roles()->syncWithPivotValues($rolesArray[$teamUser->id], ['team_id' => $teamUser->team_id, 'model_id' => $teamUser->id]);
});

})
->saveRelationshipsBeforeChildrenUsing(function ($component, $set, $get, $record) {
$record->load('users.roles');
$rolesArray = [];
foreach ($get('userss') as $userTeam) {

$rolesArray[$userTeam['id']] = $userTeam['roles'];
}

// Sync roles with team_id
$record->users()->each(function (TeamUser $teamUser) use ($rolesArray) {

$teamUser->roles()->syncWithPivotValues($rolesArray[$teamUser->id], ['team_id' => $teamUser->team_id, 'model_id' => $teamUser->id]);
});

})
replaced saveRelationshipsUsing with saveRelationshipsBeforeChildrenUsing @CodeWithDennis @Lara Zeus i've managed to eliminate that TeamUser, the question i have is, I want to have a super admin panel, where you can see resources from all Teams, the problem I see rising is this Spatie Permission relation
public function roles(): BelongsToMany
{
$relation = $this->morphToMany(
config('permission.models.role'),
'model',
config('permission.table_names.model_has_roles'),
config('permission.column_names.model_morph_key'),
app(PermissionRegistrar::class)->pivotRole
);

if (! app(PermissionRegistrar::class)->teams) {
return $relation;
}

$teamsKey = app(PermissionRegistrar::class)->teamsKey;
$relation->withPivot($teamsKey);
$teamField = config('permission.table_names.roles').'.'.$teamsKey;

return $relation->wherePivot($teamsKey, getPermissionsTeamId())
->where(fn ($q) => $q->whereNull($teamField)->orWhere($teamField, getPermissionsTeamId()));
}
public function roles(): BelongsToMany
{
$relation = $this->morphToMany(
config('permission.models.role'),
'model',
config('permission.table_names.model_has_roles'),
config('permission.column_names.model_morph_key'),
app(PermissionRegistrar::class)->pivotRole
);

if (! app(PermissionRegistrar::class)->teams) {
return $relation;
}

$teamsKey = app(PermissionRegistrar::class)->teamsKey;
$relation->withPivot($teamsKey);
$teamField = config('permission.table_names.roles').'.'.$teamsKey;

return $relation->wherePivot($teamsKey, getPermissionsTeamId())
->where(fn ($q) => $q->whereNull($teamField)->orWhere($teamField, getPermissionsTeamId()));
}
as you se it relies on getPermissionsTeamId() which you normally set when you switch the team, any idea how I can make it work in super admin panel so it brings the correct things?
CodeWithDennis
CodeWithDennis3mo ago
No sorry. 😅
jamesro
jamesroOP3mo ago
managed to sort it in the end, even simpler :))
mohdaftab
mohdaftab3mo ago
@jamesro do share the solution here please, I might need it to make it simple as well.
jamesro
jamesroOP3mo ago
this is what I've ended doing:
Forms\Components\Select::make('roles')
->preload()
->label('Roluri')
->relationship(name: 'roles', titleAttribute: 'name')
->saveRelationshipsUsing(function ($record, $state, $get) use ($form) {
setPermissionsTeamId($form->getRecord()->id);
$record->roles()->syncWithPivotValues($state, [config('permission.column_names.team_foreign_key') => $form->getRecord()->id]);
})
->multiple()
->preload()
->searchable(),
Forms\Components\Select::make('roles')
->preload()
->label('Roluri')
->relationship(name: 'roles', titleAttribute: 'name')
->saveRelationshipsUsing(function ($record, $state, $get) use ($form) {
setPermissionsTeamId($form->getRecord()->id);
$record->roles()->syncWithPivotValues($state, [config('permission.column_names.team_foreign_key') => $form->getRecord()->id]);
})
->multiple()
->preload()
->searchable(),
mohdaftab
mohdaftab3mo ago
Thank you so much.
Want results from more Discord servers?
Add your server