F
Filament3mo ago
Typhon

Roles on pivot table

What I am trying to do: Roles are handled by spatie/laravel-permission. I have two models with different roles in my project. One is User and one is OrganisationUser (pivot table between the User & Organisation). I want to show and edit this role in the relationship manager that I made for the OrganisationResource. What I did: I tried to make a text column with the name 'pivot.roles.name' because just 'roles.name' works for the UserResource. Eventually I made an attribute called rolesNames in OrgansationUser by doing 'pivot.rolesNames' I am able to show roles correct in the relationshipmanager. But this does not fix editing roles. My issue/the error: I can't seem to edit roles in the relationship manager. If I make a select the same way I did in the UserResource, it takes the roles from user. I want it to take the roles from the pivot table (OrgansationUser). Code: UsersRelationshipManager.php
Tables\Columns\TextColumn::make("pivot.rolesNames")
->label(__("labels.roles"))
->badge()
->formatStateUsing(fn($state) => __('roles.' . $state))
Tables\Columns\TextColumn::make("pivot.rolesNames")
->label(__("labels.roles"))
->badge()
->formatStateUsing(fn($state) => __('roles.' . $state))
OrganisationUser.php
<?php
class OrganisationUser extends Pivot
{
use HasRoles;

// Config files do this for the User model. We do it by setting it manually here.
protected $guard_name = 'web';

// The table needs an id so that it can be referenced by the model_has_roles table.
public $incrementing = true;

public function user(): BelongsTo
{
return $this->belongsTo(User::class)->with('roles');
}

public function organisation(): BelongsTo
{
return $this->belongsTo(Organisation::class)->with('roles');
}

public function organisationRoles(): BelongsToMany {
return $this->roles();
}

protected function rolesNames(): Attribute
{
return Attribute::make(
get: fn () => $this->roles->pluck('name'),
);
}
}
<?php
class OrganisationUser extends Pivot
{
use HasRoles;

// Config files do this for the User model. We do it by setting it manually here.
protected $guard_name = 'web';

// The table needs an id so that it can be referenced by the model_has_roles table.
public $incrementing = true;

public function user(): BelongsTo
{
return $this->belongsTo(User::class)->with('roles');
}

public function organisation(): BelongsTo
{
return $this->belongsTo(Organisation::class)->with('roles');
}

public function organisationRoles(): BelongsToMany {
return $this->roles();
}

protected function rolesNames(): Attribute
{
return Attribute::make(
get: fn () => $this->roles->pluck('name'),
);
}
}
6 Replies
Typhon
Typhon3mo ago
Tables\Columns\TextColumn::make("pivot.roles")
Tables\Columns\TextColumn::make("pivot.roles")
Does print out the roles json but pivot.roles.name returns nothing Creating your own edit action and reproducing relationship functionality works but is quite ugly:
public function editRoleAction()
{
return Tables\Actions\EditAction::make()
->mutateRecordDataUsing(function ($record, $data) {
$data['organisation_roles'] = $record->pivot->roles->pluck('id')->toArray();
return $data;
})
->modalWidth(MaxWidth::Large)
->using(function ($record, array $data) {
$roles = collect($data['organisation_roles'])->map(function ($role) {
return Role::where('id', $role)->firstOrFail()->name;
})->toArray();
$record->pivot->syncRoles($roles);
$record->update($data);

return $record;
})
->form([
Select::make("organisation_roles")
->required()
->options(
// Only get roles starting with 'psi'
Role::where('name', 'like', 'psi_%')
->pluck('name', 'id')
->mapWithKeys(fn($item, $key) => [$key => __('roles.' . $item)])
->toArray()
)
->label(__("labels.roles"))
->multiple()
->preload()
]);
}
public function editRoleAction()
{
return Tables\Actions\EditAction::make()
->mutateRecordDataUsing(function ($record, $data) {
$data['organisation_roles'] = $record->pivot->roles->pluck('id')->toArray();
return $data;
})
->modalWidth(MaxWidth::Large)
->using(function ($record, array $data) {
$roles = collect($data['organisation_roles'])->map(function ($role) {
return Role::where('id', $role)->firstOrFail()->name;
})->toArray();
$record->pivot->syncRoles($roles);
$record->update($data);

return $record;
})
->form([
Select::make("organisation_roles")
->required()
->options(
// Only get roles starting with 'psi'
Role::where('name', 'like', 'psi_%')
->pluck('name', 'id')
->mapWithKeys(fn($item, $key) => [$key => __('roles.' . $item)])
->toArray()
)
->label(__("labels.roles"))
->multiple()
->preload()
]);
}
toeknee
toeknee3mo ago
Why not use #althinect-spatie-roles-permissions
Typhon
Typhon3mo ago
@toeknee roles are on the pivot table between Users and Organisations so that users are can only do certain stuff within that organisation. This is different from having a role on User.
toeknee
toeknee3mo ago
And that's exactly why you use the teams features of the package. One team = Users, other = Organisations. Then roles are assigned per user, per 'team'
Typhon
Typhon3mo ago
@toeknee I thought the teams feature was supposed to be used for a multi-tenant setup?
toeknee
toeknee3mo ago
Sure is, but that's essentially what you are doing. But just using two tenants.