The 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. |
为了支持创建页面,将以下配置添加到 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 属性以覆盖 Form Controller 使用的默认消息。这些值可以是纯文本也可以引用一个 本地化字符串。
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 文件配置的表单控件。
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在行为配置中,design 属性控制表单的显示方式。支持以下属性。
| 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() ?>在行为配置中使用设计显示模式时,视图内容使用系统提供的标准表单内容生成。
支持以下显示模式值及其描述。
| 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;
}
});
});为了验证表单字段,你可以在模型中使用Validation trait。