F
Filamentโ€ข16mo ago
DS

is there a way to use spatie roles with teams id and filament multenancy on V3 ?

im trying to implement tenancy on v3 using filament multitenancy and also use spatie roles team functionality https://spatie.be/docs/laravel-permission/v5/basic-usage/teams-permissions at this point i can use roles and permissions and create the role with the tenant id associated butt i can'tmake it work in the relationship manager
<?php

namespace App\Filament\Panel\Resources\UserResource\RelationManagers;

use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;


class RoleRelationManager extends RelationManager
{
protected static string $relationship = 'roles';

protected static ?string $recordTitleAttribute = 'name';


public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('name')
->required()
->maxLength(255),
]);
}

public function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name'),
])
->filters([
//
])
->headerActions([
Tables\Actions\AttachAction::make()
->preloadRecordSelect()
])
->actions([
Tables\Actions\DetachAction::make(),
])
->bulkActions([
Tables\Actions\DetachBulkAction::make(),
]);
}
}
<?php

namespace App\Filament\Panel\Resources\UserResource\RelationManagers;

use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;


class RoleRelationManager extends RelationManager
{
protected static string $relationship = 'roles';

protected static ?string $recordTitleAttribute = 'name';


public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('name')
->required()
->maxLength(255),
]);
}

public function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name'),
])
->filters([
//
])
->headerActions([
Tables\Actions\AttachAction::make()
->preloadRecordSelect()
])
->actions([
Tables\Actions\DetachAction::make(),
])
->bulkActions([
Tables\Actions\DetachBulkAction::make(),
]);
}
}
any help will be appreciated ๐Ÿ™‚
73 Replies
DS
DSOPโ€ข16mo ago
@justjosef hello, here is the post!
josef
josefโ€ข16mo ago
What exactly is not working? You're not getting the tenant-specific roles? @ds_9206 ?
DS
DSOPโ€ข16mo ago
yes im not having the tenant specific roles when im trying to attach the roles to the model user even if the role has the teams_id with the team id asociated
BKF Dev
BKF Devโ€ข16mo ago
+1 Confirmed, same issue with me, the tenant_id isn't included automatically
josef
josefโ€ข16mo ago
You have to adapt the queries in the relation manager to only fetch, show, associate (and so on) the roles for the current client by scoping the used queries
BKF Dev
BKF Devโ€ข16mo ago
Hi, thanks for reply, the problem is not related to scope query, in my case, I can create roles on its CreatePanel, but if I create it using relation manager, an error says : General error: 1364 Field 'team_id' doesn't have a default value
josef
josefโ€ข16mo ago
yeah, because the field/parameter is not set. You have to set it to current team id on creation
BKF Dev
BKF Devโ€ข16mo ago
but we should not set it since we're already in the right tenant Id, you may check my repository and test it : https://github.com/aeq-dev/filamentv3-bug-7511
GitHub
GitHub - aeq-dev/filamentv3-bug-7511
Contribute to aeq-dev/filamentv3-bug-7511 development by creating an account on GitHub.
josef
josefโ€ข16mo ago
Yes, you are 'in' the right tenant, but how would the relation manager know to set an arbitrary parameter on the model (team_id on role) to the current tenant id?
BKF Dev
BKF Devโ€ข16mo ago
by the same method where we can create role without using relation manager and without setting the team id
josef
josefโ€ข16mo ago
How do you do that? In your linked repository, I don't see a RoleResource
BKF Dev
BKF Devโ€ข16mo ago
In my case it's the package user belongstomany package
josef
josefโ€ข16mo ago
I see, so it's a different question, not about the roles per se
BKF Dev
BKF Devโ€ข16mo ago
Lol yes just noticed that sorry ๐Ÿ˜„
josef
josefโ€ข16mo ago
But the same applies here. For main resources, filament handles setting of the team_id, if that corresponds to your tenant model. But the relation manager doesn't do that. You have to set it yourself, e.g. by modifying the CreateAction
BKF Dev
BKF Devโ€ข16mo ago
Thanks man
DS
DSOPโ€ข16mo ago
same for me could you ggive me an example please?
BKF Dev
BKF Devโ€ข16mo ago
Here's the code where I tried 3 methods but still doesn't work : ->headerActions([ Tables\Actions\CreateAction::make() ->before(function (array $data) { $data['school_id'] = auth()->user()->school_id; //dd($data['school_id']); return $data; }) /* ->using(function (array $data, string $model): Model { array_push($data, ['school_id' => auth()->user()->school_id]); dd($data); return $model::create($data); }) */ /* ->mutateFormDataUsing(function (array $data, RelationManager $livewire): array { $data['school_id'] = $livewire->ownerRecord->school_id; dd($data['school_id']); return $data; }), */
DS
DSOPโ€ข16mo ago
i've tried in tinkerwell the Roles::whereBelongsTo($tenant) and it works but when i try the same but instead $tenant Filament::getTenant() doesnt work even i've tried $query->whereBelongsTo($tenant) after tried with Roles::whereBelongsTo($tenant it returns the right roles for the tenant but doesnt save it to the db im stuck at that point
BKF Dev
BKF Devโ€ข16mo ago
Yes this is the problem
DS
DSOPโ€ข16mo ago
i've tried on the method ->mutateFormDataUsing(function (array $data, RelationManager $livewire): array { $data['school_id'] = $livewire->ownerRecord->school_id; dd($data['school_id']); return $data; }), obviously with my data, i've copied the method from above on ly for example
josef
josefโ€ข16mo ago
@ds_9206 that should work, if you set team_id to Filament::getTenant()->id
BKF Dev
BKF Devโ€ข16mo ago
@ds_9206 if it works please share with us your code ๐Ÿ™‚
DS
DSOPโ€ข16mo ago
it works to get the roles with ->mutateFormDataUsing(function (Builder $query): array { return Roles::whereBelongsTo(Filament::getTenant()) }), but that it's not saving to the database the role attached to the user also im using the attachaction not the createaction as you can see in my example above (1st message of this thread)
josef
josefโ€ข16mo ago
wait, so you're not creating a role, but attaching an existing one to a model? Then the role should already have the team_id set
DS
DSOPโ€ข16mo ago
but it's not when i create the roles it works fine im just trying to attach the role to the user
josef
josefโ€ข16mo ago
and then what error do you get? something's off here can you share code?
DS
DSOPโ€ข16mo ago
yes, please wait
<?php

namespace App\Filament\Panel\Resources\UserResource\RelationManagers;

use App\Models\Role;
use Filament\Facades\Filament;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Query\Builder;

class RoleRelationManager extends RelationManager
{
protected static string $relationship = 'roles';

protected static ?string $recordTitleAttribute = 'name';

public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('name')
->required()
->maxLength(255),
]);
}

public function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name'),
])
->filters([
//
])
->headerActions([
Tables\Actions\AttachAction::make()->recordSelectOptionsQuery(function (Builder $builder) {
return Role::whereBelongsTo(Filament::getTenant());
}),
])
->actions([
Tables\Actions\DetachAction::make(),
])
->bulkActions([
Tables\Actions\DetachBulkAction::make(),
]);
}
}
<?php

namespace App\Filament\Panel\Resources\UserResource\RelationManagers;

use App\Models\Role;
use Filament\Facades\Filament;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Query\Builder;

class RoleRelationManager extends RelationManager
{
protected static string $relationship = 'roles';

protected static ?string $recordTitleAttribute = 'name';

public function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('name')
->required()
->maxLength(255),
]);
}

public function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name'),
])
->filters([
//
])
->headerActions([
Tables\Actions\AttachAction::make()->recordSelectOptionsQuery(function (Builder $builder) {
return Role::whereBelongsTo(Filament::getTenant());
}),
])
->actions([
Tables\Actions\DetachAction::make(),
])
->bulkActions([
Tables\Actions\DetachBulkAction::make(),
]);
}
}
this works for the attach dialog but not when i save the attachment
DS
DSOPโ€ข16mo ago
when i click on the attach button it doesnt attach
josef
josefโ€ข16mo ago
Is there an error?
DS
DSOPโ€ข16mo ago
Nope, just doesn't attach to the user
DS
DSOPโ€ข16mo ago
this is when i do
->mutateFormDataUsing(function (array $data, RelationManager $livewire): array {

dd($data);
return $data;
}),
->mutateFormDataUsing(function (array $data, RelationManager $livewire): array {

dd($data);
return $data;
}),
josef
josefโ€ข16mo ago
Is there a new entry in the model_has_roles table?
DS
DSOPโ€ข16mo ago
nope, the same entry i doesnt create anything even using ->using(function (array $data, string $model): Model {
return $model::create($data); }) it doesn't create nothing I've done thios
->using(function (array $data, string $model): Model {

$role = Role::find($data['recordId']);


auth()->user()->assignRole([$role->name]);

return $role;
}),
->using(function (array $data, string $model): Model {

$role = Role::find($data['recordId']);


auth()->user()->assignRole([$role->name]);

return $role;
}),
`
DS
DSOPโ€ข16mo ago
but i got this
DS
DSOPโ€ข16mo ago
following the spatie docs for attach a role to a user
josef
josefโ€ข16mo ago
have you set the global team_id as per the docs?
DS
DSOPโ€ข16mo ago
yes I've in a middleware this setPermissionsTeamId(Filament::getTenant()->id); $user = Auth::user(); // unset cached model relations so new team relations will get reloaded $user->unsetRelation('roles', 'permissions'); this is the entire middleware <?php namespace App\Http\Middleware; use App\Models\Company; use Auth; use Closure; use Filament\Facades\Filament; use Illuminate\Http\Request; class UserCanAccessToTenantMiddleware { public function handle(Request $request, Closure $next) { if (Auth::check()) { $isTenantOwner = $request->user()->company->contains(function (Company $value, int $key) { return $value->id === Filament::getTenant()->id; }); if ($isTenantOwner || $request->user()->hasRole('Super Admin')) { setPermissionsTeamId(Filament::getTenant()->id); $user = Auth::user(); // unset cached model relations so new team relations will get reloaded $user->unsetRelation('roles', 'permissions'); return $next($request); } else { return abort(404,); } } return $next($request); } }
josef
josefโ€ข16mo ago
what's the content of your roles table?
DS
DSOPโ€ข16mo ago
im only working with the "administrador" role
josef
josefโ€ข16mo ago
this - to me - is the smoking gun. I think there's something else going on, nothing to do with Filament. If you can't assign the role to the user manually, it won't work in the Relationmanager I'd check the stack trace to see what's going on and why it can't find the Administrador role, even if it exists
DS
DSOPโ€ข16mo ago
im getting this after a few seconds on the attachment modal
DS
DSOPโ€ข16mo ago
also the Administrador role is there it's just doesn't store in the db
DS
DSOPโ€ข16mo ago
if i attach the role manual it doesn't show on the relation tabe
DS
DSOPโ€ข16mo ago
DS
DSOPโ€ข16mo ago
also im using this trait for tenancy roles with filament <?php namespace App\Models\Support; use App\Models\Company; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Spatie\Permission\Traits\HasRoles as SpatieHasRoles; trait HasRoles { use SpatieHasRoles; public function rolesOnTenant(Company $tenant): BelongsToMany { $originalTenantId = getPermissionsTeamId(); setPermissionsTeamId($tenant); $roles = $this->roles(); setPermissionsTeamId($originalTenantId); return $roles; } public function syncRolesOnTenant( Company $tenant, array|\Spatie\Permission\Contracts\Role|\Illuminate\Support\Collection|string|int $roles ): self { $originalTenantId = getPermissionsTeamId(); setPermissionsTeamId($tenant); $this->syncRoles($roles); setPermissionsTeamId($originalTenantId); return $this; } public function assignRoleOnTenant( Company $tenant, array|string|int|\Spatie\Permission\Contracts\Role|\Illuminate\Support\Collection $role ): self { $originalTenantId = getPermissionsTeamId(); setPermissionsTeamId($tenant); $this->assignRole($role); setPermissionsTeamId($originalTenantId); return $this; } } At the moment I appreciate your help, it's a little late in my city and I'm dying of sleep and in part that doesn't help me to think well, I appreciate your help and I hope tomorrow I can continue counting on it, I wish you have a good day / night as appropriate
josef
josefโ€ข16mo ago
ok, you're doing so much custom stuff, it's hard to grasp what might be wrong But as I see it, this is not a filament-related problem have a nice night!
DS
DSOPโ€ข16mo ago
ialso think this is not a filament problem, im just asking for help on how can i integrate that with filament really i love how it works filament and i would like to implement this im thinking to continue searching how to do this thanks a lot
josef
josefโ€ข16mo ago
Please do! I think it's an issue of how you're using the spatie package, maybe ask there, or in the general laravel discord. But I'd mark this question as resolved as it's not related to filament itself. If you get the problem figured out, the approach I showed you will work, I'm sure. Otherwise you can ask again, and I give permission to mention me in that case.
BKF Dev
BKF Devโ€ข16mo ago
For me, I've just resolved it by adding a hidden input ๐Ÿ™‚ Hidden::make('school_id') ->default($this->ownerRecord->school_id),
awcodes
awcodesโ€ข16mo ago
While this may solve youโ€™re issue. It is unsafe to do anything with idโ€™s in hidden fields since they can be manipulated on the front end before saving the data back to the database.
BKF Dev
BKF Devโ€ข16mo ago
Thanks for help but it doesn't work :/
JamesTechDude
JamesTechDudeโ€ข16mo ago
With Filament, it's difficult to understand sometimes, but everything is based on the team_id already that you don't need to assign a team_id in roles. I would get rid of that completely on the roles side and change your tables to have the team_id field, or have pivot tables like post_user for Posts that belong to a User The rest is handled at the highest level of multi tenancy As far as I know, there isn't a way yet to have multiple Roles with multiple Teams for 1 User You can only have 1 Role with multiple Teams for 1 User. The rest is handled through the specific table themselves (like a posts table), where a team_id is set to the specific record (or through a pivot table) This has taken me a lot to understand too, so I get where you're coming from, but there's no way to set the team_id to the roles table as far as I've found. Instead, it's set at the resource level (Posts, Articles, etc) and in their specific tables or through pivot relationship which is still only using the team ID and the Model ID (no Role ID) The only time you need team_id in the role assignment table, is when 1 User has multiple Roles for different Teams You can also use Policies (through laravel Policies) to setup permissions but that's pretty much it - as far as I know @BKF Dev @DS
DS
DSOPโ€ข16mo ago
I understand and I thank you for the explanation, but in my case I need the role by team, for example, a role can exist in a team (company in my case) but not in another. That is why I need it, until now I have made it work only when I want to "attach" a role to a user, the role relationship manager returns only those that have been related to that team regardless of whether they belong to that team or not. @JamesAutoDude when i do
Tables\Actions\AttachAction::make()
->preloadRecordSelect()
->recordSelectOptionsQuery(function (Builder $query) {
$roles = Role::where('company_id', Filament::getTenant()->id)->get()->pluck('id')->toArray();
return $query->whereIn('company_id', $roles);
})
Tables\Actions\AttachAction::make()
->preloadRecordSelect()
->recordSelectOptionsQuery(function (Builder $query) {
$roles = Role::where('company_id', Filament::getTenant()->id)->get()->pluck('id')->toArray();
return $query->whereIn('company_id', $roles);
})
` i got SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'company_id' in where clause is ambiguous
JamesTechDude
JamesTechDudeโ€ข16mo ago
@DS So I see you are trying to put all roles into an array that belong to that specific company... I would move this to the Company model as a function:
public function roles()
{
$roles = Role::where('company_id', Filament::getTenant()->id)->get()->pluck('id')->toArray();

return $roles;
}
public function roles()
{
$roles = Role::where('company_id', Filament::getTenant()->id)->get()->pluck('id')->toArray();

return $roles;
}
And then you can just call that relationship instead of running the query directly, especially so it's in 1 central location in case you need to change it in the future for all spots that need to grab the roles... Let me see about the rest though Do you maybe have a demo repo of this? @tuto1902 Do you think you can assist with this? ๐Ÿ˜ฎ I know you know a ton about this stuff ๐Ÿ™‚ https://filamentphp.com/docs/3.x/panels/resources/relation-managers#attaching-and-detaching-records @DS also check that relationship and make sure it's returning what it should, use dd($roles) in controller function (before the return) and it should output the values it grabbed I think I wrote the relationship incorrectly as well :/
tuto1902
tuto1902โ€ข16mo ago
Sure @JamesAutoDude Iโ€™ll get myself acquainted with the issue in this post before I can offer a response. Iโ€™ll do my best to assist ๐Ÿ‘๐Ÿป
DS
DSOPโ€ข16mo ago
@JamesAutoDude it returns roles but when do that i got
SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'company_id' in where clause is ambiguous
SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'company_id' in where clause is ambiguous
` I've a repo but its a private repo if you need i can give you part of the related code or i can make a new public repo replicating the problem if you want if i remove the teams functionality from spatie everything work as spected but i really need to use spatie roles teams functionality with filament any help will be really appreciated
JamesTechDude
JamesTechDudeโ€ข16mo ago
I think you may want to remove the teams functionality from Spatie and utilize Filament to do the rest for you. We just need to figure out how to set the company_id to a user/role
DS
DSOPโ€ข16mo ago
as a suggestion we can add the company_id to the roles and use a custom HasRoles trait to change the roles for each company ... if i can make it work i would make a PR to filament hoping it works for someone else, i really like filament and i want to somehow be able to contribute something. Thanks for your help. @JamesAutoDude
tuto1902
tuto1902โ€ข16mo ago
I don't really have anything else to add on this one. The HasRoles trait sounds like a good plan. ๐Ÿ‘๐Ÿป
BKF Dev
BKF Devโ€ข16mo ago
I think the best solution is here in this pr : https://github.com/bezhanSalleh/filament-shield/pull/216 We may discover all resources & pages from all panels, and then we can access to shied page on one admin panel
GitHub
Discovery by makzumi ยท Pull Request #216 ยท bezhanSalleh/filament-sh...
Hi Bezhan, Followed your lead from my last PR and I added a new feature to show all resources, pages, widgets from different panels in a single panel. Hope this one makes it, Thanks Added new confi...
DS
DSOPโ€ข16mo ago
i dont think that will be the best solution @BKF Dev because in that solution to the problem its not related to this one... it would be a work around but not solves my problem, anyway thanks for sharing this here... @tuto1902 i've implemented the HasRoles trait and it works but sometimes i got some errors
tuto1902
tuto1902โ€ข16mo ago
What kind of errors are you getting?
DS
DSOPโ€ข16mo ago
i could find it, i've make a post here in the discord channel but it says something related to the query, please let me find it sorry, it was here ๐Ÿ˜’ the error is this "SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'company_id' in where clause is ambiguous " im using "company" as the relation for tenancy
BKF Dev
BKF Devโ€ข16mo ago
Coud you share the HasRoles trait you've coded ?
DS
DSOPโ€ข16mo ago
hello! yes, sure,
<?php

namespace App\Models\Support;

use App\Models\Company;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Spatie\Permission\Traits\HasRoles as SpatieHasRoles;

trait HasRoles
{
use SpatieHasRoles;

public function rolesOnTenant(Company $tenant): BelongsToMany
{
$originalTenantId = getPermissionsTeamId();

setPermissionsTeamId($tenant);

$roles = $this->roles();

setPermissionsTeamId($originalTenantId);

return $roles;
}

public function syncRolesOnTenant(
Company $tenant,
array|\Spatie\Permission\Contracts\Role|\Illuminate\Support\Collection|string|int $roles
): self
{
$originalTenantId = getPermissionsTeamId();

setPermissionsTeamId($tenant);

$this->syncRoles($roles);

setPermissionsTeamId($originalTenantId);

return $this;
}

public function assignRoleOnTenant(
Company $tenant,
array|string|int|\Spatie\Permission\Contracts\Role|\Illuminate\Support\Collection $role
): self
{
$originalTenantId = getPermissionsTeamId();

setPermissionsTeamId($tenant);

$this->assignRole($role);

setPermissionsTeamId($originalTenantId);

return $this;
}
}
<?php

namespace App\Models\Support;

use App\Models\Company;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Spatie\Permission\Traits\HasRoles as SpatieHasRoles;

trait HasRoles
{
use SpatieHasRoles;

public function rolesOnTenant(Company $tenant): BelongsToMany
{
$originalTenantId = getPermissionsTeamId();

setPermissionsTeamId($tenant);

$roles = $this->roles();

setPermissionsTeamId($originalTenantId);

return $roles;
}

public function syncRolesOnTenant(
Company $tenant,
array|\Spatie\Permission\Contracts\Role|\Illuminate\Support\Collection|string|int $roles
): self
{
$originalTenantId = getPermissionsTeamId();

setPermissionsTeamId($tenant);

$this->syncRoles($roles);

setPermissionsTeamId($originalTenantId);

return $this;
}

public function assignRoleOnTenant(
Company $tenant,
array|string|int|\Spatie\Permission\Contracts\Role|\Illuminate\Support\Collection $role
): self
{
$originalTenantId = getPermissionsTeamId();

setPermissionsTeamId($tenant);

$this->assignRole($role);

setPermissionsTeamId($originalTenantId);

return $this;
}
}
BKF Dev
BKF Devโ€ข16mo ago
Thanks, I think spatie permission has team_id on its config file, we can use it as tenant_id
DS
DSOPโ€ข16mo ago
it can be changed as the proyect needs and as you say in the config, it can be used as the tenant_id
Want results from more Discord servers?
Add your server