个性化 Filament Profile 页面:扩展设计并集成多个表单

默认情况下,Filament Profile 页面很简单。它看起来与登录和注册屏幕非常相似,具有简单直接的界面,没有任何侧菜单或其他元素。

制作定制的 Edit Profile 页面

运行命令: php artisan make:filament-page EditProfile --type=custom

执行该命令将生成两个文件: App\Filament\Pages\EditProfile.php - 这用作 Livewire 组件的 PHP 类,包含核心逻辑。

resources\filament\pages\edit-profile.blade.php - 这是前端的视图文件,用于制作用户界面。

随后,是时候重新配置 Filament 面板设置了,将配置文件链接引导到我们新创建的页面。

app/Providers/Filament/AdminPanelProvider.php

php 复制代码
use App\Filament\Pages\EditProfile;  
class AdminPanelProvider extends PanelProvider  
{  
    public function panel(Panel $panel): Panel  
    {  
        return $panel  
        ->default()  
        ->id('admin')  
        ->path('admin')  
        ->login()  
        ->profile()  
        ->userMenuItems([  
        'profile' => MenuItem::make()->url(fn (): string => EditProfile::getUrl())  
        ])  
        // More configurations...  
    }  
}

导航到"编辑个人资料"部分后,迎接您的是一个精美的自定义页面,标题优雅地为"编辑个人资料"。

接下来,让我们使用多个表单来丰富我们的自定义页面。此页面是一个 Livewire 组件,因此您需要使其可用于表单。让我们来探讨一下如何操作。

为表单准备页面 在 Ur app/Filament/Pages/EditProfile.php 中,添加以下内容:

php 复制代码
use Filament\Forms\Contracts\HasForms;  
use Filament\Forms\Concerns\InteractsWithForms;  
class EditProfile extends Page implements HasForms  
{  
use InteractsWithForms;  
protected static string $view = 'filament.pages.edit-profile';  
protected static bool $shouldRegisterNavigation = false;  
}

现在,让我们定义并填写表单。处理多个表单时的主要区别在于使用 getForms() 方法声明表单名称。此方法返回一个表单名称数组,这些名称对应于您需要为每个表单定义的公共属性。

修改EditProfile.php

php 复制代码
use Exception;  
use Filament\Forms;  
use Filament\Forms\Form;  
use Filament\Pages\Page;  
use Filament\Pages\Concerns;  
use Filament\Actions\Action;  
use Filament\Facades\Filament;  
use Illuminate\Support\Facades\Hash;  
use Illuminate\Database\Eloquent\Model;  
use Illuminate\Validation\Rules\Password;  
use Illuminate\Contracts\Auth\Authenticatable;  
class EditProfile extends Page implements HasForms  
{  
use InteractsWithForms;  
protected static string $view = 'filament.pages.edit-profile';  
protected static bool $shouldRegisterNavigation = false;  
public ?array $profileData = [];  
public ?array $passwordData = [];  
public function mount(): void  
{  
$this->fillForms();  
}  
protected function getForms(): array  
{  
return [  
'editProfileForm',  
'editPasswordForm',  
];  
}  
public function editProfileForm(Form $form): Form  
{  
return  
->schema([  
Forms\Components\Section::make('Profile Information')  
->description('Update your account\'s profile information and email address.')  
->schema([  
Forms\Components\TextInput::make('name')  
->required(),  
Forms\Components\TextInput::make('email')  
->email()  
->required()  
->unique(ignoreRecord: true),  
]),  
])  
->model($this->getUser())  
->statePath('profileData');  
}  
public function editPasswordForm(Form $form): Form  
{  
return  
->schema([  
Forms\Components\Section::make('Update Password')  
->description('Ensure your account is using long, random password to stay secure.')  
->schema([  
Forms\Components\TextInput::make('Current password')  
->password()  
->required()  
->currentPassword(),  
Forms\Components\TextInput::make('password')  
->password()  
->required()  
->rule(Password::default())  
->autocomplete('new-password')  
->dehydrateStateUsing(fn ($state): string => Hash::make($state))  
->live(debounce: 500)  
->same('passwordConfirmation'),  
Forms\Components\TextInput::make('passwordConfirmation')  
->password()  
->required()  
->dehydrated(false),  
]),  
])  
->model($this->getUser())  
->statePath('passwordData');  
}  
protected function getUser(): Authenticatable & Model  
{  
$user = Filament::auth()->user();  
if (! $user instanceof Model) {  
throw new Exception('The authenticated user object must be an Eloquent model to allow the profile page to update it.');  
}  
return $user;  
}  
protected function fillForms(): void  
{  
$data = $this->getUser()->attributesToArray();  
$this->editProfileForm->fill($data);  
$this->editPasswordForm->fill();  
}  
}

在上面的代码片段中,editProfileFormeditPasswordForm 是定义每个表单的公共方法。它们的名称对应于您在 getForms() 方法中指定的表单名称。

最后,是时候让这些形式成为众人瞩目的焦点了。修改您的 resources/views/filament/pages/edit-profile.blade.php

php 复制代码
<x-filament-panels::page>  
<x-filament-panels::form>  
{{ $this->editProfileForm }}  
</x-filament-panels::form>  
<x-filament-panels::form>  
{{ $this->editPasswordForm }}  
</x-filament-panels::form>  
</x-filament-panels::page>

在设置后返回 Edit Profile 页面后,您将看到两个整齐分割的表单,可供用户交互。然而,这些表格缺乏可操作的按钮。

增强 Filament 中的表单提交

在保存表单数据之前,您需要提供一种启动该过程的方法 --- 通常是提交按钮。

app/Filament/Pages/EditProfile.php

php 复制代码
use Filament\Actions\Action;  
class EditProfile extends Page implements HasForms  
{  
//...  
protected function getUpdateProfileFormActions(): array  
{  
return [  
Action::make('updateProfileAction')  
->label(__('filament-panels::pages/auth/edit-profile.form.actions.save.label'))  
->submit('editProfileForm'),  
];  
}  
protected function getUpdatePasswordFormActions(): array  
{  
return [  
Action::make('updatePasswordAction')  
->label(__('filament-panels::pages/auth/edit-profile.form.actions.save.label'))  
->submit('editPasswordForm'),  
];  
}  
//...  
}

显示提交操作 resources/views/filament/pages/edit-profile.blade.php

php 复制代码
<x-filament-panels::page>  
<x-filament-panels::form wire:submit="updateProfile">  
{{ $this->editProfileForm }}  
<x-filament-panels::form.actions  
:actions="$this->getUpdateProfileFormActions()"  
/>  
</x-filament-panels::form>  
<x-filament-panels::form wire:submit="updatePassword">  
{{ $this->editPasswordForm }}  
<x-filament-panels::form.actions  
:actions="$this->getUpdatePasswordFormActions()"  
/>  
</x-filament-panels::form>  
</x-filament-panels::page>

通过这种集成,表单不仅可见,而且还附有用于提交数据的功能操作按钮。 处理提交的数据

提交表单只是第一步。需要验证、处理数据,并可能存储数据。

php 复制代码
class EditProfile extends Page implements HasForms  
{  
//...  
public function updateProfile(): void  
{  
$data = $this->editProfileForm->getState();  
$this->handleRecordUpdate($this->getUser(), $data);  
$this->sendSuccessNotification();  
}  
public function updatePassword(): void  
{  
$data = $this->editPasswordForm->getState();  
$this->handleRecordUpdate($this->getUser(), $data);  
if (request()->hasSession() && array_key_exists('password', $data)) {  
request()->session()->put(['password_hash_' . Filament::getAuthGuard() => $data['password']]);  
}  
$this->editPasswordForm->fill();  
$this->sendSuccessNotification();  
}  
private function handleRecordUpdate(Model $record, array $data): Model  
{  
$record->update($data);  
return $record;  
}  
}

updateProfileupdatePassword 方法管理每个表单的提交过程。

在密码更新的情况下,更新会话的 password_hash_ 以防止意外的身份验证错误至关重要。

更新后,最好重置表单,确保后续条目的记录清晰可见。

提供反馈是用户体验不可或缺的一个方面。成功更新用户的个人资料或密码后,应通知用户。

私有函数 sendSuccessNotification():void

php 复制代码
{  
Notification::make()  
->success()  
->title(__('filament-panels::pages/auth/edit-profile.notifications.saved.title'))  
->send();  
}

实现现代表单样式

通过将 ->aside() 方法附加到表单的部分,您可以立即转换表单的外观。

类 EditProfile 扩展了 Page 实现 HasForms

php 复制代码
{  
// ...  
public function editProfileForm(Form $form): Form  
{  
return $form  
->schema([  
Forms\Components\Section::make('Profile Information')  
->aside() // This is the magic line!  
->description('Update your account\'s profile information and email address.')  
->schema([  
// Your fields here...  
]),  
])  
->model($this->getUser())  
->statePath('profileData');  
}  
public function editPasswordForm(Form $form): Form  
{  
return $form  
->schema([  
Forms\Components\Section::make('Update Password')  
->aside() // Again, for the modern touch!  
->description('Ensure your account uses a long, random password for optimal security.')  
->schema([  
// Your fields here...  
]),  
])  
->model($this->getUser())  
->statePath('passwordData');  
}  
// Additional methods...  
}

->aside() 方法有效地更改了表单部分的布局,将部分标题和描述移到一边(因此称为"aside"),并将实际的表单输入放在右侧。这导致了更紧凑、更现代的设计,既具有视觉吸引力又对用户友好。

相关推荐
Q_192849990610 分钟前
基于Spring Boot的九州美食城商户一体化系统
java·spring boot·后端
ZSYP-S39 分钟前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
Yuan_o_1 小时前
Linux 基本使用和程序部署
java·linux·运维·服务器·数据库·后端
程序员一诺2 小时前
【Python使用】嘿马python高级进阶全体系教程第10篇:静态Web服务器-返回固定页面数据,1. 开发自己的静态Web服务器【附代码文档】
后端·python
DT辰白2 小时前
如何解决基于 Redis 的网关鉴权导致的 RESTful API 拦截问题?
后端·微服务·架构
thatway19893 小时前
AI-SoC入门:15NPU介绍
后端
陶庵看雪3 小时前
Spring Boot注解总结大全【案例详解,一眼秒懂】
java·spring boot·后端
Q_19284999063 小时前
基于Spring Boot的图书管理系统
java·spring boot·后端
ss2733 小时前
基于Springboot + vue实现的汽车资讯网站
vue.js·spring boot·后端
一只IT攻城狮4 小时前
华为云语音交互SIS的使用案例(文字转语音-详细教程)
java·后端·华为云·音频·语音识别