The first page and Filament Resource in this course will be for the list of users. Also, we will add a filter based on the user role.
We will also add a role restriction: all the actions like creating new administrator users, changing passwords, and deactivating users will be accessible only for users with the "administrator" role.
Adding User Soft Deletes
When the user is deactivated, they will be deleted using Soft Deletes. So first, let's add softDeletes
to the User
model.
php artisan make:migration "add soft deletes to users table"
database/migrations/xxx_add_soft_deletes_to_users_table:
public function up(): void{ Schema::table('users', function (Blueprint $table) { $table->softDeletes(); });}
app/Models/User.php:
use Illuminate\Database\Eloquent\SoftDeletes; class User extends Authenticatable implements FilamentUser{ use SoftDeletes; // ...}
Filament User Table and Form
Now let's move on to the Filament part. First, we need to create a Resource.
php artisan make:filament-resource User
We won't be allowing to edit users, so you can delete the app\Filament\Resources\UserResource\Pages\EditUser.php
file. Then we also need to remove the page from the UserResource
.
app/Filament/Resources/UserResource.php:
class UserResource extends Resource{ // ... public static function getPages(): array { return [ 'index' => Pages\ListUsers::route('/'), 'create' => Pages\CreateUser::route('/create'), 'edit' => Pages\EditUser::route('/{record}/edit'), ]; }}
Now let's add a Filament Form.
app/Filament/Resources/UserResource.php:
use Illuminate\Validation\Rules\Password; class UserResource extends Resource{ protected static ?string $model = User::class; protected static ?string $navigationIcon = 'heroicon-o-user'; public static function form(Form $form): Form { return $form ->schema([ Forms\Components\TextInput::make('name') ->required(), Forms\Components\TextInput::make('email') ->email() ->required() ->unique(), Forms\Components\TextInput::make('password') ->required() ->password() ->maxLength(255) ->rule(Password::default()) ]); } // ...}
This will create a form like the one below:
Mutate User Data Before Saving
Now that we have a form, we need to do two things when creating a new user:
- Hash the password before saving it to the DB.
- Set the role to the
admin
because we will only manage administrators from the admin panel.
To do this, we will add a mutateFormDataBeforeCreate()
method in the CreateUser
file. Also, we will a little info text that this form will create an administrator user.
app/Filament/Resources/UserResource/Pages/CreateUser.php:
use App\Models\Role;use Illuminate\Support\Facades\Hash;use Illuminate\Contracts\Support\Htmlable; class CreateUser extends CreateRecord{ protected static string $resource = UserResource::class; protected function getSubheading(): string|Htmlable|null { return 'This form will create an administrator user'; } protected function mutateFormDataBeforeCreate(array $data): array { $data['password'] = Hash::make($data['password']); $data['role_id'] = Role::ROLE_ADMINISTRATOR; return $data; }}
Table Columns and Sorting
Next, let's add columns to the Users table and make the default sort by created_at
field, descending.
app/Filament/Resources/UserResource.php:
class UserResource extends Resource{ // ... public static function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('name') ->searchable(), Tables\Columns\TextColumn::make('role.name'), Tables\Columns\TextColumn::make('created_at') ->dateTime(), ]) ->filters([ // ]) ->actions([ Tables\Actions\EditAction::make(), ]) ->bulkActions([ Tables\Actions\DeleteBulkAction::make(), ]) ->defaultSort('created_at', 'desc'); } // ...}
Now we have a table.
Actions: Change Password and Deactivate User
Now let's add two actions: instead of the default edit
action, we need actions for changing the password and deactivating the user.
These two actions will be visible only if the user role is Administrator
.
app/Filament/Resources/UserResource.php:
use App\Models\Role;use Filament\Tables\Actions\Action;use Illuminate\Support\Facades\Hash;use Illuminate\Validation\Rules\Password; class UserResource extends Resource{ // ... public static function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('name') ->searchable(), Tables\Columns\TextColumn::make('role.name'), Tables\Columns\TextColumn::make('created_at') ->dateTime(), ]) ->filters([ // ]) ->actions([ Tables\Actions\EditAction::make(), Action::make('changePassword') ->action(function (User $record, array $data): void { $record->update([ 'password' => Hash::make($data['new_password']), ]); Filament::notify('success', 'Password changed successfully.'); }) ->form([ Forms\Components\TextInput::make('new_password') ->password() ->label('New Password') ->required() ->rule(Password::default()), Forms\Components\TextInput::make('new_password_confirmation') ->password() ->label('Confirm New Password') ->rule('required', fn($get) => ! ! $get('new_password')) ->same('new_password'), ]) ->icon('heroicon-o-key') ->visible(fn (User $record): bool => $record->role_id === Role::ROLE_ADMINISTRATOR), Action::make('deactivate') ->color('danger') ->icon('heroicon-o-trash') ->action(fn (User $record) => $record->delete()) ->visible(fn (User $record): bool => $record->role_id === Role::ROLE_ADMINISTRATOR), ]) ->bulkActions([ Tables\Actions\DeleteBulkAction::make(), ]) ->defaultSort('created_at', 'desc'); } // ...}
Now we have two actions:
The Deactivate
action will soft-delete the user, and the Change password
will open a modal to change the user's password.
Table Filter for Roles
Last thing for this table: let's add a filter for the user role.
app/Filament/Resources/UserResource.php:
class UserResource extends Resource{ // ... public static function table(Table $table): Table { return $table ->columns([ Tables\Columns\TextColumn::make('name') ->searchable(), Tables\Columns\TextColumn::make('role.name'), Tables\Columns\TextColumn::make('created_at') ->dateTime(), ]) ->filters([ Tables\Filters\SelectFilter::make('role') ->options([ Role::ROLE_USER => 'User', Role::ROLE_OWNER => 'Owner', Role::ROLE_ADMINISTRATOR => 'Administrator', ]) ->attribute('role_id'), ]) ->actions([ Action::make('changePassword') ->action(function (User $record, array $data): void { $record->update([ 'password' => Hash::make($data['new_password']), ]); Filament::notify('success', 'Password changed successfully.'); }) ->form([ Forms\Components\TextInput::make('new_password') ->password() ->label('New Password') ->required() ->rule(Password::default()), Forms\Components\TextInput::make('new_password_confirmation') ->password() ->label('Confirm New Password') ->rule('required', fn($get) => ! ! $get('new_password')) ->same('new_password'), ]) ->icon('heroicon-o-key') ->visible(fn (User $record): bool => $record->role_id === Role::ROLE_ADMINISTRATOR), Action::make('deactivate') ->color('danger') ->icon('heroicon-o-trash') ->action(fn (User $record) => $record->delete()) ->visible(fn (User $record): bool => $record->role_id === Role::ROLE_ADMINISTRATOR), ]) ->bulkActions([ Tables\Actions\DeleteBulkAction::make(), ]) ->defaultSort('created_at', 'desc'); } // ...}
This will add a select filter with the three roles as an option.
getSubheading() does not match \vendor\filament\filament\src\Resources\Pages\CreateRecord.php there is a getTitle() ??
This is the link to the exact place in the source.
app/Filament/Resources/UserResource/Pages/CreateUser.php
There is a better option for the filter, if there is a realtionship. Instead of manually doing this
Allow admin to change all passwords:
And deactivate other users only:
I can't find anything about Filament::notify() to resolve this error here:
Because here it uses the old syntax. Check the docs for notification and you will