Backend\Behaviors\FormController 类是一个控制器行为,用于轻松地向后端页面添加表单功能。该行为提供三个页面,分别名为 Create、Update 和 Preview。Preview 页面是 Update 页面的只读版本。当您使用表单行为时,无需在控制器中定义 create、update 和 preview 操作 - 该行为会为您完成。但是您应该提供相应的视图文件。
表单行为依赖于表单字段定义和一个模型类。为了使用表单行为,你应该将其添加到控制器类的 $implement 属性。此外, $formConfig 类属性应该被定义并且其值应该指向用于配置行为属性的YAML文件。
namespace Acme\Blog\Controllers;
class Categories extends \Backend\Classes\Controller
{
public $implement = [
\Backend\Behaviors\FormController::class
];
public $formConfig = 'config_form.yaml';
}通常表单和列表控制器在同一个控制器中一起使用。
在 $formConfig 属性中引用的配置文件采用 YAML 格式定义。 该文件应放置在 控制器视图目录 中。 下面是一个典型的表单行为配置文件示例。
# config_form.yaml
name: Blog Category
form: $/acme/blog/models/post/fields.yaml
modelClass: Acme\Blog\Post
create:
title: New Blog Post
update:
title: Edit Blog Post
preview:
title: View Blog Post表单配置文件中需要以下属性。
| Property | Description |
|---|---|
| name | the name of the object being managed by this form. |
| form | a configuration array or reference to a form field definition file, see form fields. |
| modelClass | a model class name, the form data is loaded and saved against this model. |
下列配置属性是可选的。如果您希望表单行为支持创建、更新或预览页面,请定义这些属性。
| Property | Description |
|---|---|
| design | display the form using a specific form design mode when rendering (see below). |
| defaultRedirect | used as a fallback redirection page when no specific redirect page is defined. |
| create | a configuration array or reference to a config file for the Create page. |
| update | a configuration array or reference to a config file for the Update page. |
| preview | a configuration array or reference to a config file for the Preview page. |
| customMessages | customize the messages used in the Form Controllers. |
| permissions | apply restrictions to certain actions provided by the Form Controller. |
为了支持 Create 页面,将以下配置添加到 YAML 文件。
create:
title: New Blog Post
redirect: acme/blog/posts/update/:id
redirectClose: acme/blog/posts以下属性支持创建页面。
| Property | Description |
|---|---|
| title | a page title, can refer to a localization string. |
| redirect | redirection page when record is saved. |
| redirectClose | redirection page when record is saved and the close post variable is sent with the request. |
| form | overrides the default form fields definitions for the create page only. |
为支持更新页面,请将以下配置添加到 YAML 文件中。
update:
title: Edit Blog Post
redirect: acme/blog/posts更新页面支持以下属性。
| Property | Description |
|---|---|
| title | a page title, can refer to a localization string. |
| redirect | redirection page when record is saved. |
| redirectClose | redirection page when record is saved and close post variable is sent with the request. |
| form | overrides the default form fields definitions for the update page only. |
为支持预览页面,请在 YAML 文件中添加以下配置:
preview:
title: View Blog Post预览页面支持以下属性。
| Property | Description |
|---|---|
| title | a page title, can refer to a localization string. |
| form | overrides the default form fields definitions for the preview page only. |
指定 customMessages 属性以覆盖表单控制器使用的默认消息。这些值可以是纯文本,也可以引用一个 本地化字符串。
customMessages:
notFound: Did not find the thing
flashCreate: New thing created
flashUpdate: Updated that thing
flashDelete: Thing is gone您还可以修改正在显示的表单上下文中的消息。 以下将仅针对 update 上下文覆盖 notFound 消息。
update:
customMessages:
notFound: Nothing found when updating以下消息可作为自定义消息进行覆盖。
::: details 查看可用消息的列表
| Message | Default Message |
|---|---|
| notFound | Form record with an ID of :id could not be found. |
| flashCreate | :name Created |
| flashUpdate | :name Updated |
| flashDelete | :name Deleted |
| ::: |
指定 permissions 属性以对表单控制器提供的操作施加限制。使用 权限值,当前后端用户必须拥有这些权限才能使用该字段。支持单个权限的字符串或权限数组,其中只需一个即可授予访问权限。
permissions:
modelCreate: admins.manage.create
modelDelete: admins.manage.delete以下属性可作为所需权限进行重写。
::: details 查看可用消息列表
| Message | Default Message |
|---|---|
| modelCreate | required to create new records. |
| modelUpdate | required to modify existing records. |
| modelPreview | required to preview existing records. |
| modelDelete | required to delete existing records. |
| ::: |
可用的表单字段属性可以在 表单字段定义 页面上找到。
表单字段使用 YAML 文件定义。表单字段配置由表单行为使用,用于创建表单控件并将其绑定到模型字段。
该文件被放置到某个插件的 models 目录的一个子目录中。该子目录的名称匹配以小写形式书写的模型类名。文件名不重要,但 fields.yaml 和 form_fields.yaml 是常用名称。示例表单字段文件位置:
├── plugins
| └── acme
| └── blog
| └── models
| ├── post ← 配置目录
| | └── fields.yaml ← 配置文件
| └── Post.php ← 模型类
字段可以放置在三个区域,外部区域、主标签页或次级标签页。下一个示例展示了一个表单字段定义文件的典型内容。
# fields.yaml
fields:
blog_title:
label: Blog Title
description: The title for this blog
published_at:
label: Published date
description: When this blog post was published
type: datepicker
# [...]
tabs:
fields:
# [...]
secondaryTabs:
fields:
# [...]对于你的表单支持创建、更新和预览的每个页面你应该提供一个视图文件具有对应的名称 - create.php、update.php 和 preview.php。
表单行为向控制器类添加了两个方法:formRender,formRenderDesign 和 formRenderPreview。这些方法渲染上述YAML文件配置的表单控件。
The create.php 视图表示创建页面,允许用户创建新记录。典型的创建页面包含面包屑、表单本身和表单按钮。data-request 属性应引用表单行为提供的 onSave AJAX 处理程序。下面是典型的创建视图文件的内容。
<?= Form::open(['class' => 'd-flex flex-column h-100']) ?>
<div class="flex-grow-1">
<?= $this->formRender() ?>
</div>
<div class="form-buttons">
<div data-control="loader-container">
<button
type="button"
data-request="onSave"
data-request-data="{ close: true }"
data-request-message="Creating Category..."
data-hotkey="ctrl+enter, cmd+enter"
class="btn btn-default">
Create and Close
</button>
<span class="btn-text">
or <a href="<?= Backend::url('acme/blog/categories') ?>">Cancel</a>
</span>
</div>
</div>
<?= Form::close() ?>若要跟踪未保存的更改并在离开表单时显示警告,请在表单的开始标签上包含 data-change-monitor 属性。
<?= Form::open(['class' => '...', 'data-change-monitor' => true]) ?>update.php 视图表示更新页面,允许用户更新或删除现有记录。典型的更新页面包含面包屑导航、表单本身和表单按钮。更新页面与创建页面非常相似,但通常有删除按钮。该 data-request 属性应该引用由表单行为提供的 onSave AJAX 处理程序。下面是典型的 update.php 表单的内容。
<?= Form::open(['class' => 'd-flex flex-column h-100']) ?>
<div class="flex-grow-1">
<?= $this->formRender() ?>
</div>
<div class="form-buttons">
<div data-control="loader-container">
<button
type="button"
data-request="onSave"
data-request-data="{ close: true }"
data-request-message="Saving Category..."
data-hotkey="ctrl+enter, cmd+enter"
class="btn btn-default">
Save and Close
</button>
<button
type="button"
class="oc-icon-trash-o btn-icon danger pull-right"
data-request="onDelete"
data-request-message="Deleting Category..."
data-request-confirm="Do you really want to delete this category?">
</button>
<span class="btn-text">
or <a href="<?= Backend::url('acme/blog/categories') ?>">Cancel</a>
</span>
</div>
</div>
<?= Form::close() ?>preview.php 视图代表允许用户以只读模式预览现有记录的预览页面。一个典型的预览页面包含面包屑和表单本身。下面是典型的 preview.php 表单的内容。
<div class="form-preview">
<?= $this->formRenderPreview() ?>
</div>create:controller 命令生成 控制器 并支持 --design 选项以实现所需的显示模式, 如下所述.
php artisan create:controller Acme.Blog Posts --design=popup表单设计在您需要显示表单而无需管理 HTML 内容时很有用,这虽然灵活性较低,但可以加快表单构建过程。
design:
displayMode: basic设计属性,在行为配置中,控制表单的显示方式。支持以下属性。
| Property | Description |
|---|---|
| displayMode | specifies the display mode to use, supported values: custom, basic, survey, sidebar, popup. Default: basic |
| horizontalMode | show form fields in horizontal orientation. Default: false |
| surveyMode | disables tabs and displays all fields on the page in sections with headers. Default: false |
| size | size of the page container, supported values: 50 stepped increments between 400-1200, auto. Default: auto |
| sidebarSize | width of the sidebar in sidebar mode, supported values 50 steps between 300-750. Default: 300 |
使用 formRenderDesign 方法渲染表单设计在 **create.php**, **update.php** 和preview.php` 视图文件内。
<?= $this->formRenderDesign() ?>当在行为配置中使用设计显示模式时,视图内容将使用系统提供的标准表单内容生成。
以下 displayMode 值受支持 以及其描述。
| Display Mode | Description |
|---|---|
| custom | Render the form using custom view files (default) |
| basic | Basic layout with for standard forms |
| survey | Survey layout using stacked sections with headings |
| sidebar | Sidebar layout where secondary tabs are rendered in the side panel |
| popup | Form contents are managed inside popup windows |
尺寸 属性定义了页面容器的尺寸或弹窗的尺寸。
design:
displayMode: survey
size: 950如果设计被设置为使用popup显示模式,那么你完全不需要创建任何视图文件。所有表单管理功能都包含在一个弹出窗口内。
design:
displayMode: popup
size: 750当与 列表控制器 集成时,将 recordOnClick 属性设置为 popup 以在点击一条记录时打开管理视图。
# config_list.yaml
recordOnClick: popup该 recordOnClick 也支持传递一个上下文到表单控制器,例如,将值设置为 popup@preview 用于预览上下文。
# config_list.yaml
recordOnClick: popup@preview创建视图可以通过使用 onLoadPopupForm AJAX 处理程序并结合弹窗控件来打开,如下例所示。
<button
type="button"
data-control="popup"
data-handler="onLoadPopupForm"
class="btn btn-primary">
New Item
</button>有时您可能希望修改默认的表单行为,有几种方法可以实现此目的。
您可以动态地使用 formGetConfig 方法扩展表单配置。
public function formGetConfig()
{
$config = $this->asExtension('FormController')->formGetConfig();
$config->form = $this->makeConfig($config->form);
// Set the active tab dynamically
$config->form->tabs['activeTab'] = 'Activities';
return $config;
}您可以在控制器中的 create、update 或 preview 动作方法中使用您自己的逻辑,然后选择性地调用表单行为的父方法。
public function update($recordId, $context = null)
{
//
// Do any custom code here
//
// Call the FormController behavior update() method
return $this->asExtension('FormController')->update($recordId, $context);
}您可以使用 formBeforeSave 重写(或等效方法)来更改表单在其保存或更新之前的保存值。要重写字段的保存值 使用 formSetSaveValue(key, value) 方法。
public function formBeforeSave($model)
{
// When locale dropdown is set to "custom", override with the _custom_locale text field
if (post('MyModel[locale]') === 'custom') {
$this->formSetSaveValue('locale', post('MyModel[_custom_locale]'));
}
}您可以通过覆盖 formGetRedirectUrl 方法来指定模型保存后重定向的 URL。此方法返回要重定向的位置,其中相对 URL 会被视为后端 URL。
public function formGetRedirectUrl($context = null, $model = null)
{
return 'https://octobercms.com';
}表单的查找查询 数据库模型 可以通过覆盖控制器类中的 formExtendQuery 方法来扩展。这个例子将确保软删除的记录仍然可以被找到和更新,通过将 withTrashed 作用域应用到查询中:
public function formExtendQuery($query)
{
$query->withTrashed();
}您可以通过绑定到 backend.form.extendFields 全局事件来从外部扩展另一个控制器的字段。事件函数将接收一个 $form 参数,该参数代表 Backend\Widgets\Form 对象,您可以使用 getController,getModel 和 getContext 方法来检查执行上下文。
由于此事件可能影响所有表单,因此务必检查控制器和模型是否为正确类型。以下是一个使用 addFields 方法向邮件设置表单添加新字段的示例。
Event::listen('backend.form.extendFields', function($form) {
if (
!$form->getController() instanceof \System\Controllers\Settings ||
!$form->getModel() instanceof \System\Models\MailSetting
) {
return;
}
$form->addFields([
'my_field' => [
'label' => 'My Field',
'comment' => 'This is a custom field I have added.',
],
]);
});您还可以通过在控制器类中重写 formExtendFields 方法来内部扩展表单字段。这将只影响 FormController 行为所使用的表单。
class Categories extends \Backend\Classes\Controller
{
public $implement = [
\Backend\Behaviors\FormController::class
];
public function formExtendFields($form)
{
$form->addFields([...]);
}
}以下方法在 $form 对象上可用。
| Method | Description |
|---|---|
| addFields | adds new fields to the outside area |
| addTabFields | adds new fields to the tabbed area |
| addSecondaryTabFields | adds new fields to the secondary tabbed area |
| removeField | remove a field from any areas |
每个方法接受一个字段数组,类似于表单字段配置。
正如字段依赖关系部分中所述, 您也可以通过扩展, 通过挂接到 form.filterFields 事件来实现表单字段过滤.
User::extend(function ($model) {
$model->bindEvent('model.form.filterFields', function ($formWidget, $fields, $context) use ($model) {
if ($model->source_type === 'http') {
$fields->source_url->hidden = false;
$fields->git_branch->hidden = true;
}
elseif ($model->source_type === 'git') {
$fields->source_url->hidden = false;
$fields->git_branch->hidden = false;
}
else {
$fields->source_url->hidden = true;
$fields->git_branch->hidden = true;
}
});
});验证表单的字段时,你可以在模型中使用 验证特性。