Laravel Nova/Filament 的自定义字段与组件开发:扩展后台管理界面的能力
大家好,今天我们来深入探讨 Laravel Nova 和 Filament 这两个强大的后台管理框架的自定义字段与组件开发。我们将分析它们各自的架构,并通过实际的代码示例,展示如何通过自定义字段和组件来扩展它们的能力,从而构建更符合项目需求的后台管理界面。
Nova 自定义字段开发
Laravel Nova 提供了强大的字段自定义能力。通过自定义字段,我们可以实现各种复杂的数据输入和展示需求。Nova 的字段本质上是 Vue.js 组件的封装,因此我们需要熟悉 Vue.js 的基础知识。
1. 创建自定义字段类
首先,我们需要创建一个继承自 LaravelNovaFieldsField 的自定义字段类。这个类负责定义字段的行为,例如如何存储数据、如何展示数据等等。
<?php
namespace AppNovaFields;
use LaravelNovaFieldsField;
class CustomTextField extends Field
{
/**
* The field's component.
*
* @var string
*/
public $component = 'custom-text-field';
/**
* Resolve the given attribute from the given resource.
*
* @param mixed $resource
* @param string $attribute
* @return mixed
*/
public function resolveAttribute($resource, $attribute)
{
return $resource->{$attribute};
}
/**
* Hydrate the given attribute on the model based on the incoming request.
*
* @param IlluminateHttpRequest $request
* @param string $requestAttribute
* @param object $model
* @param string $attribute
* @return void
*/
public function fillAttributeFromRequest(Request $request, $requestAttribute, $model, $attribute)
{
if ($request->exists($requestAttribute)) {
$model->{$attribute} = $request->input($requestAttribute);
}
}
/**
* Define any extra serialization data for the field.
*
* @return array
*/
public function jsonSerialize(): array
{
return array_merge(parent::jsonSerialize(), [
'foo' => 'bar', // 传递给 Vue 组件的额外数据
]);
}
}
$component: 定义了该字段使用的 Vue 组件的名称。resolveAttribute(): 定义了如何从模型中获取字段的值。fillAttributeFromRequest(): 定义了如何将请求中的数据保存到模型中。jsonSerialize(): 定义了传递给 Vue 组件的额外数据。
2. 创建 Vue 组件
接下来,我们需要创建一个 Vue 组件,用于渲染自定义字段。这个组件需要处理数据的显示和编辑。
<template>
<div>
<label :for="id">{{ label }}</label>
<input
:id="id"
type="text"
class="w-full form-control form-input form-input-bordered"
:value="value"
@input="updateValue($event.target.value)"
/>
<p v-if="foo">{{ foo }}</p>
</div>
</template>
<script>
export default {
props: ['resource', 'resourceName', 'resourceId', 'field', 'value'],
mounted() {
console.log('CustomTextField mounted');
},
computed: {
id() {
return `custom-text-field-${this.field.attribute}`;
},
label() {
return this.field.name;
},
foo() {
return this.field.foo;
}
},
methods: {
updateValue(value) {
this.$emit('input', value);
},
},
};
</script>
props: 接收来自 Nova 的数据,例如resource,resourceName,resourceId,field,value。computed: 用于计算组件的属性,例如id,label。methods: 用于定义组件的方法,例如updateValue,用于将用户输入的值传递给 Nova。
3. 注册 Vue 组件
我们需要将 Vue 组件注册到 Nova 中。可以在 app/Providers/NovaServiceProvider.php 文件的 boot 方法中进行注册。
public function boot()
{
parent::boot();
Nova::script('custom-text-field', asset('js/components/CustomTextField.js'));
}
4. 使用自定义字段
现在,我们可以在 Nova 资源中使用自定义字段了。
<?php
namespace AppNova;
use AppNovaFieldsCustomTextField;
use LaravelNovaResource;
use LaravelNovaFieldsID;
use IlluminateHttpRequest;
class Post extends Resource
{
/**
* The model the resource corresponds to.
*
* @var string
*/
public static $model = AppModelsPost::class;
/**
* The single value that should be used to represent the resource when being displayed.
*
* @var string
*/
public static $title = 'title';
/**
* The columns that should be searched.
*
* @var array
*/
public static $search = [
'title',
];
/**
* Get the fields displayed by the resource.
*
* @param IlluminateHttpRequest $request
* @return array
*/
public function fields(Request $request)
{
return [
ID::make(__('ID'), 'id')->sortable(),
CustomTextField::make('Title', 'title'),
];
}
}
表格总结 Nova 自定义字段开发步骤:
| 步骤 | 描述 | 代码示例 |
|---|---|---|
| 1. 创建自定义字段类 | 继承 LaravelNovaFieldsField,定义字段行为。 |
class CustomTextField extends Field { ... } |
| 2. 创建 Vue 组件 | 创建 Vue 组件,用于渲染字段的 UI。 | <template>...</template><script>...</script> |
| 3. 注册 Vue 组件 | 在 NovaServiceProvider 中注册 Vue 组件。 |
Nova::script('custom-text-field', asset('js/components/CustomTextField.js')); |
| 4. 使用自定义字段 | 在 Nova 资源中使用自定义字段。 | CustomTextField::make('Title', 'title') |
Filament 自定义字段开发
Filament 也提供了强大的自定义字段能力,称为 Custom Form Components。与 Nova 类似,Filament 的自定义字段也是基于 Livewire 和 Alpine.js 构建的。
1. 创建自定义字段组件
首先,我们需要创建一个 Livewire 组件,用于渲染自定义字段。
<?php
namespace AppLivewireForms;
use FilamentFormsComponentsComponent;
use FilamentFormsContractsHasForms;
use FilamentFormsGet;
use FilamentFormsSet;
use LivewireComponent;
class CustomTextField extends Component implements HasForms
{
use FilamentFormsConcernsInteractsWithForms;
public ?string $state = null;
public string $fieldId = 'custom-text-field';
public string $label = 'Custom Text Field';
public function mount(): void
{
$this->form->fill([
'state' => $this->state,
]);
}
public function render()
{
return view('livewire.forms.custom-text-field');
}
public function getStatePath(): string
{
return 'state';
}
}
state: 用于存储字段的值。fieldId: 字段的 ID,用于关联 label 和 input。label: 字段的标签。mount(): 在组件挂载时执行,用于初始化表单。render(): 渲染组件的视图。getStatePath(): 用于返回组件状态的路径,对于使用->live()很有用。
2. 创建 Blade 视图
接下来,我们需要创建一个 Blade 视图,用于渲染自定义字段的 UI。
<div>
<label for="{{ $fieldId }}">{{ $label }}</label>
<input
type="text"
id="{{ $fieldId }}"
wire:model.live="state"
class="filament-input block w-full border-gray-300 focus:border-primary-500 focus:ring-primary-500 rounded-md shadow-sm"
/>
</div>
wire:model.live="state": 将 input 的值绑定到组件的state属性。
3. 在 Filament 资源中使用自定义字段
现在,我们可以在 Filament 资源中使用自定义字段了。
<?php
namespace AppFilamentResources;
use AppFilamentResourcesPostResourcePages;
use AppModelsPost;
use FilamentForms;
use FilamentFormsForm;
use FilamentResourcesResource;
use FilamentTables;
use FilamentTablesTable;
use AppLivewireFormsCustomTextField;
class PostResource extends Resource
{
protected static ?string $model = Post::class;
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
public static function form(Form $form): Form
{
return $form
->schema([
FormsComponentsTextInput::make('title')
->required()
->maxLength(255),
CustomTextField::make('custom_field') // 使用自定义字段
->label('Custom Field Label')
->statePath('custom_field'), //指定state存储的字段
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
TablesColumnsTextColumn::make('title'),
TablesColumnsTextColumn::make('created_at')
->dateTime(),
TablesColumnsTextColumn::make('updated_at')
->dateTime(),
])
->filters([
//
])
->actions([
TablesActionsEditAction::make(),
])
->bulkActions([
TablesActionsBulkActionGroup::make([
TablesActionsDeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => PagesListPosts::route('/'),
'create' => PagesCreatePost::route('/create'),
'edit' => PagesEditPost::route('/{record}/edit'),
];
}
}
CustomTextField::make('custom_field'): 创建自定义字段实例,custom_field是字段的名称,用于存储数据。label('Custom Field Label'): 设置字段的标签。statePath('custom_field'): 绑定组件的state属性到模型的custom_field属性。
表格总结 Filament 自定义字段开发步骤:
| 步骤 | 描述 | 代码示例 |
|---|---|---|
| 1. 创建 Livewire 组件 | 创建 Livewire 组件,用于渲染字段的 UI。 | class CustomTextField extends Component implements HasForms { ... } |
| 2. 创建 Blade 视图 | 创建 Blade 视图,用于渲染字段的 UI。 | <div>...</div> |
| 3. 在 Filament 资源中使用自定义字段 | 在 Filament 资源中使用自定义字段。 | CustomTextField::make('custom_field')->label('Custom Field Label') |
Nova 自定义组件开发
除了自定义字段,Nova 还允许我们开发自定义组件,用于扩展后台管理界面的功能。例如,我们可以创建一个自定义面板,用于展示统计数据。
1. 创建自定义组件
首先,我们需要创建一个 Vue 组件,用于渲染自定义组件。
<template>
<div class="card">
<h3 class="font-bold mb-2">{{ title }}</h3>
<p>{{ description }}</p>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
},
mounted() {
console.log('CustomComponent mounted');
},
};
</script>
props: 接收来自 Nova 的数据,例如title,description。
2. 注册 Vue 组件
我们需要将 Vue 组件注册到 Nova 中。可以在 app/Providers/NovaServiceProvider.php 文件的 boot 方法中进行注册。
public function boot()
{
parent::boot();
Nova::component('custom-component', asset('js/components/CustomComponent.js'));
}
3. 创建 Nova 工具
接下来,我们需要创建一个 Nova 工具,用于将自定义组件添加到 Nova 界面中。
<?php
namespace AppNovaTools;
use LaravelNovaTool;
class CustomTool extends Tool
{
/**
* Perform any tasks that need to happen when the tool is booted.
*
* @return void
*/
public function boot()
{
Nova::script('custom-tool', asset('js/tool.js'));
}
/**
* Build the view that renders the navigation links for the tool.
*
* @return IlluminateViewView
*/
public function renderNavigation()
{
return view('nova.tools.custom-tool');
}
}
boot(): 在工具启动时执行,用于注册 JavaScript 文件。renderNavigation(): 渲染工具的导航链接。
4. 创建 Blade 视图
我们需要创建一个 Blade 视图,用于渲染工具的导航链接。
<router-link to="/custom-tool" class="block py-3 px-6 text-sm font-semibold text-gray-800 hover:bg-gray-100">
Custom Tool
</router-link>
5. 创建 Nova 路由
我们需要创建一个 Nova 路由,用于渲染自定义组件。
<?php
namespace AppNova;
use IlluminateSupportFacadesRoute;
use LaravelNovaNova;
use LaravelNovaTool;
class CustomTool extends Tool
{
/**
* Perform any tasks that need to happen when the tool is booted.
*
* @return void
*/
public function boot()
{
Nova::script('custom-tool', asset('js/tool.js'));
}
/**
* Build the view that renders the navigation links for the tool.
*
* @return IlluminateViewView
*/
public function renderNavigation()
{
return view('nova.tools.custom-tool');
}
/**
* Register the tool's routes.
*
* @param array $routes
* @return array
*/
public static function routes()
{
Route::get('/custom-tool', function () {
return Nova::render('custom-tool');
});
}
}
6. 创建自定义工具视图
我们需要创建一个自定义工具视图,它使用自定义组件。
<template>
<div>
<h1>Custom Tool</h1>
<custom-component title="Welcome" description="This is a custom component in Nova." />
</div>
</template>
<script>
export default {
mounted() {
console.log('CustomTool view mounted');
},
};
</script>
7. 注册 Nova 工具
我们需要将 Nova 工具注册到 config/nova.php 文件中。
<?php
return [
// ...
'tools' => [
new AppNovaToolsCustomTool,
],
];
表格总结 Nova 自定义组件开发步骤:
| 步骤 | 描述 | 代码示例 |
|---|---|---|
| 1. 创建 Vue 组件 | 创建 Vue 组件,用于渲染自定义组件的 UI。 | <template>...</template><script>...</script> |
| 2. 注册 Vue 组件 | 在 NovaServiceProvider 中注册 Vue 组件。 |
Nova::component('custom-component', asset('js/components/CustomComponent.js')); |
| 3. 创建 Nova 工具 | 创建 Nova 工具,用于将自定义组件添加到 Nova 界面中。 | class CustomTool extends Tool { ... } |
| 4. 创建 Blade 视图 | 创建 Blade 视图,用于渲染工具的导航链接。 | <router-link to="/custom-tool">...</router-link> |
| 5. 创建 Nova 路由 | 创建 Nova 路由,用于渲染自定义组件。 | Route::get('/custom-tool', function () { ... }); |
| 6. 创建自定义工具视图 | 创建自定义工具视图,它使用自定义组件。 | <custom-component title="Welcome" description="This is a custom component in Nova." /> |
| 7. 注册 Nova 工具 | 将 Nova 工具注册到 config/nova.php 文件中。 |
'tools' => [ new AppNovaToolsCustomTool, ], |
Filament 定制化组件开发
Filament 提供了许多可定制的组件,例如 actions, columns, filters 等。我们可以通过扩展这些组件来满足特定的需求。
1. 定制化 Actions
可以创建自定义 actions 用于在 Filament 的 table 和 form 中执行特定的操作。
<?php
namespace AppFilamentActions;
use FilamentActionsAction;
class CustomAction extends Action
{
public static function make(string $name): static
{
return parent::make($name)
->label('Custom Action')
->icon('heroicon-o-pencil-square')
->requiresConfirmation()
->action(function () {
// 执行自定义操作
Log::info('Custom action executed!');
});
}
}
然后在 resource 的 Table 或 Form 中使用:
public static function table(Table $table): Table
{
return $table
->columns([
// ...
])
->actions([
AppFilamentActionsCustomAction::make('custom'),
])
->bulkActions([
// ...
]);
}
2. 定制化 Columns
定制化 Columns 允许您自定义表格中数据的展示方式。
<?php
namespace AppFilamentColumns;
use FilamentTablesColumnsColumn;
class CustomColumn extends Column
{
protected function setUp(): void
{
$this->label('Custom Column')
->formatStateUsing(function ($state) {
return "Custom: " . $state;
});
}
}
在 resource 的 Table 中使用:
public static function table(Table $table): Table
{
return $table
->columns([
AppFilamentColumnsCustomColumn::make('name'),
])
->actions([
// ...
])
->bulkActions([
// ...
]);
}
3. 定制化 Filters
通过定制 Filters,可以为表格添加更灵活的筛选功能。
<?php
namespace AppFilamentFilters;
use FilamentTablesFiltersFilter;
use IlluminateDatabaseEloquentBuilder;
class CustomFilter extends Filter
{
public static function make(string $name): static
{
return parent::make($name)
->label('Custom Filter')
->query(function (Builder $query): Builder {
return $query->where('status', 'active');
});
}
}
在 resource 的 Table 中使用:
public static function table(Table $table): Table
{
return $table
->columns([
// ...
])
->filters([
AppFilamentFiltersCustomFilter::make('custom'),
])
->actions([
// ...
])
->bulkActions([
// ...
]);
}
表格总结 Filament 定制化组件开发步骤:
| 步骤 | 描述 | 代码示例 |
|---|---|---|
| 1. 定制化 Actions | 创建自定义 actions 用于在 Filament 的 table 和 form 中执行特定的操作。 | class CustomAction extends Action { ... } AppFilamentActionsCustomAction::make('custom') |
| 2. 定制化 Columns | 定制化 Columns 允许您自定义表格中数据的展示方式。 | class CustomColumn extends Column { ... } AppFilamentColumnsCustomColumn::make('name') |
| 3. 定制化 Filters | 通过定制 Filters,可以为表格添加更灵活的筛选功能。 | class CustomFilter extends Filter { ... } AppFilamentFiltersCustomFilter::make('custom') |
总结:灵活扩展,构建强大后台
通过以上示例,我们可以看到 Laravel Nova 和 Filament 都提供了强大的自定义字段和组件开发能力。无论是自定义字段,组件,actions, columns, filters,我们都可以根据项目需求,灵活地扩展后台管理界面的功能,构建更符合业务需求的后台管理系统。
编写可维护的自定义组件
编写可维护的自定义字段和组件需要遵循一些最佳实践,例如清晰的代码结构、详细的文档、以及充分的测试。
持续学习与探索
Laravel Nova 和 Filament 都在不断发展,保持学习和探索的热情,才能更好地利用它们的功能,构建出更优秀的后台管理界面。