#Add roles / permissions per team for user

32 messages · Page 1 of 1 (latest)

thick crown
#

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

    {
        return $this->belongsToMany(Team::class, 'team_user')->using(TeamUser::class);
    }

Team - Model

public function users()
    {
        return $this->hasMany(TeamUser::class);
    }```


TeamUser - Model

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

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


```php
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

worthy shadowBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> ✅ Mark Solution

thick crown
#

Do I also need to remove team_id from primary I suppose also right ?

torn kite
#

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

thick crown
#

The sql error is solved but team_id field remains empty

torn kite
#

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]
    );
})
thick crown
#

@torn kite 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'),
            ]);
    }```
torn folio
#

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);
}
// Multi-Tenancy
public function companies(): BelongsToMany
{
  return $this->belongsToMany(Company::class);
}
#

I use ⁠ #bezhansalleh-shield with multi-tenancy works pretty good.

lethal grove
torn folio
torn folio
#

Im using it on the "employee" panel, but on my "customer" panel it also works? It all depends on your policies, right?

thick crown
#

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);
    }```

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')
                        ])
                    ])```

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..
torn folio
#

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.

thick crown
#

@torn kite using saveRelationshipsUsing on the repeater, does nothing

trying to place a dd right after

->saveRelationshipsUsing(function ($component, $set, $get, $record) { 
dd('die');
})

it goes by and inserts a value in model_has_roles with team_id missing

torn folio
#

Is this something that can help?

thick crown
#

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 

```php
->saveRelationshipsUsing(function ($component, $set, $get, $record) {
dd($get('userss')); 
})
thick crown
#

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]);
                        });

                    })

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

#

@torn folio @torn kite any idea ? 😢

tacit smelt
#

have you tried setting up setPermissionTeamId($team_id) before setting roles?

thick crown
#

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]);
                        });

                    })```

replaced saveRelationshipsUsing with saveRelationshipsBeforeChildrenUsing
thick crown
#

@torn folio @torn kite 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()));
    }```

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?
torn folio
#

No sorry. 😅

thick crown
#

managed to sort it in the end, even simpler :))

tacit smelt
#

@thick crown do share the solution here please, I might need it to make it simple as well.

thick crown
#

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(),