Installation
composer require alessandronuunes/filament-member
php artisan filament-member:install
php artisan migrate
Configure User Model
Add HasTenantRelations trait and HasTenants contract to your User model:
use AlessandroNuunes\FilamentMember\Traits\HasTenantRelations;
use Filament\Models\Contracts\HasTenants;
class User extends Authenticatable implements FilamentUser, HasTenants
{
use HasTenantRelations;
// ...
}
Register Plugin Add to your Filament panel configuration:
public function panel(Panel $panel): Panel
{
return $panel->plugins([
MemberPlugin::make(),
]);
}
Invite a new member to your tenant:
// Invite members via API (if needed)
$invites = TenantInvite::create([
'tenant_id' => $tenant->id,
'email' => 'user@example.com',
'role' => TenantRole::Admin,
]);
// Bulk invite (via Filament UI)
$emails = ['user1@example.com', 'user2@example.com'];
$roles = [TenantRole::Member, TenantRole::Member];
MemberInvitation::bulkInvite($tenant, $emails, $roles);
// Change a member's role
$member->updateRole(TenantRole::Admin);
// Check if user can access a tenant
if ($user->canAccessTenant($tenant)) {
// Grant access
}
// Get all tenants a user owns or is a member of
$tenants = $user->getTenants($panel);
// Check if user is an owner of a tenant
if ($user->isOwnerOf($tenant)) {
// Owner-specific logic
}
// Override default invite behavior (e.g., require registration)
event(new TenantInviteCreated($invite));
User model implements HasTenants.Listen to events for custom logic:
// Listen for new invitations
TenantInviteCreated::subscribe(function (TenantInviteCreated $event) {
// Custom logic (e.g., log, audit, or trigger workflows)
});
Expose an API endpoint to accept invitations:
Route::get('/invite/{token}/accept', [InviteController::class, 'accept'])
->middleware(['signed', 'throttle:60']);
Extend the default notification logic:
// Override the notification sender
TenantInviteCreated::subscribe(function (TenantInviteCreated $event) {
TenantInviteNotification::send($event->invite);
});
Leverage the built-in bulk invite feature:
// In a Filament action or API endpoint
MemberInvitation::bulkInvite($tenant, $emails, $roles);
App\Models\Tenant, update config/filament-member.php:
'models' => [
'tenant' => App\Models\Organization::class,
],
ConfigHelper::getTenantModel() to verify the correct model is loaded.HasTenants ContractHasTenants on the User model will break tenancy.use Filament\Models\Contracts\HasTenants;
use AlessandroNuunes\FilamentMember\Traits\HasTenantRelations;
class User extends Authenticatable implements FilamentUser, HasTenants
{
use HasTenantRelations;
}
'owner' not 1).HasLabel and HasColor:
enum TenantRole: string
{
case Owner = 'owner';
case Admin = 'admin';
case Member = 'member';
public function label(): string
{
return match ($this) {
self::Owner => 'Owner',
self::Admin => 'Admin',
self::Member => 'Member',
};
}
public function color(): string
{
return match ($this) {
self::Owner => 'danger',
self::Admin => 'warning',
self::Member => 'info',
};
}
}
invite_expiration_days (default: 7). Set to null for no expiration:
'invites' => [
'expire_after_days' => null,
],
AlreadyMember validation rule prevents re-inviting existing members.MemberInvitation::inviteIfNotMember($tenant, $email, $role).@source directive.resources/css/filament/admin/theme.css:
@source '../../../../vendor/alessandronuunes/filament-member/resources/views/filament/**/*';
Verify your config/filament-member.php matches expectations:
php artisan config:clear
Debug invitation creation:
TenantInviteCreated::subscribe(function (TenantInviteCreated $event) {
\Log::info('Invite created', ['invite' => $event->invite]);
});
Ensure relationships are correctly set up:
// Check if a user can access a tenant
if (!$user->canAccessTenant($tenant)) {
\Log::error('User cannot access tenant', [
'user_id' => $user->id,
'tenant_id' => $tenant->id,
]);
}
Manually test the invitation flow:
$invite->token./invite/{token}/accept (or custom path).accepted_at timestamp updates.Override the default invite creation:
// In a service provider
TenantInvite::creating(function ($invite) {
$invite->custom_field = 'value';
});
Add custom columns to the Filament table:
use AlessandroNuunes\FilamentMember\Tables\MemberTable;
MemberTable::modify(function (Table $table) {
$table->addColumns([
Tables\Columns\TextColumn::make('custom_field')
->label('Custom Data'),
]);
});
Listen for tenant creation to auto-invite owners:
TenantCreated::subscribe(function ($tenant) {
$owner = auth()->user();
$tenant->members()->attach($owner->id, ['role' => TenantRole::Owner]);
});
Extend the default notification:
// Publish and override the notification view
php artisan vendor:publish --tag=filament-member-views
// Then edit `resources/views/vendor/filament-member/email/invite.blade.php`
Extend the member table with custom actions:
MemberTable::modify(function (Table $table) {
$table->actions([
Tables\Actions\Action::make('customAction')
->action(function (Member $member) {
// Custom logic
}),
]);
});
Add translations for unsupported locales:
php artisan vendor:publish --tag=filament-member-translations
// Edit `lang/vendor/filament-member/es/default.php`
How can I help you explore Laravel packages today?