个性化 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"),并将实际的表单输入放在右侧。这导致了更紧凑、更现代的设计,既具有视觉吸引力又对用户友好。

相关推荐
uzong3 小时前
技术故障复盘模版
后端
GetcharZp4 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程4 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研4 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi5 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国6 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy6 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
AntBlack6 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt
bobz9657 小时前
pip install 已经不再安全
后端
寻月隐君7 小时前
硬核实战:从零到一,用 Rust 和 Axum 构建高性能聊天服务后端
后端·rust·github