该 Backend\Behaviors\ListController 类是一个控制器行为,用于轻松地向页面添加记录列表。该行为提供可排序和可搜索的列表,其记录上带有可选链接。该行为提供控制器动作 index 但是该列表可以在任何地方渲染,并且可以使用多个列表定义。
列表行为取决于列表列定义和一个模型类。为了使用列表行为 你应该将其添加到控制器类的$implement属性中。此外,$listConfig 类属性应该被定义并且它的值应该指向用于配置行为属性的 YAML 文件。
namespace Acme\Blog\Controllers;
class Categories extends \Backend\Classes\Controller
{
public $implement = [
\Backend\Behaviors\ListController::class
];
public $listConfig = 'config_list.yaml';
}常常列表和表单控制器会在同一个控制器中一起使用。
配置文件中引用的 $listConfig 属性定义为 YAML 格式。该文件应放置在控制器的 视图目录。下面是一个典型的列表行为配置文件示例。
# config_list.yaml
title: Blog Posts
list: ~/plugins/acme/blog/models/post/columns.yaml
modelClass: Acme\Blog\Models\Post
recordUrl: acme/blog/posts/update/:id以下属性在列表配置文件中是必需的。
| Property | Description |
|---|---|
| title | a title for this list. |
| list | a configuration array or reference to a list column definition file, see list columns. |
| modelClass | a model class name, the list data is loaded from this model. |
以下列出的配置属性为可选。
| Property | Description |
|---|---|
| filter | filter configuration, see list filters. |
| recordUrl | link each list record to another page. Eg: users/update:id. The :id part is replaced with the record identifier. This allows you to link the list behavior and the form behavior. |
| recordOnClick | custom JavaScript code to execute when clicking on a record. |
| noRecordsMessage | a message to display when no records are found, can refer to a localization string. |
| deleteMessage | a message to display when records are bulk deleted, can refer to a localization string. |
| noRecordsDeletedMessage | a message to display when a bulk delete action is triggered, but no records were deleted, can refer to a localization string. |
| recordsPerPage | records to display per page, use 0 for no pages. Default: 0 |
| perPageOptions | options for number of items per page. Default: [20, 40, 80, 100, 120] |
| showPageNumbers | displays page numbers with pagination. Disable this to improve list performance when working with large tables. Default: true |
| toolbar | reference to a Toolbar Widget configuration file, or an array with configuration (see below). |
| showSorting | displays the sorting link on each column. Default: true |
| defaultSort | sets a default sorting column and direction when user preference is not defined. Supports a string or an array with keys column and direction. The direction can be asc for ascending (default) or desc for descending order. |
| showCheckboxes | displays checkboxes next to each record. Default: false. |
| showSetup | displays the list column set up button. Default: false. |
| structure | enables a structured list, see the sorting records article for more details. |
| customViewPath | specify a custom view path to override partials used by the list, optional. |
| customPageName | specify a custom variable name to use in the page URL for paginated records. Set to false to disable storing the page number in the URL. Default: page. |
若要在列表中包含工具栏,请将以下配置添加到列表配置 YAML 文件中:
toolbar:
buttons: list_toolbar
search:
prompt: Find records工具栏配置允许:
| Property | Description |
|---|---|
| buttons | a reference to a controller partial file with the toolbar buttons. Eg: _list_toolbar.htm |
| search | reference to a Search Widget configuration file, or an array with configuration. |
搜索配置支持以下属性:
| Property | Description |
|---|---|
| prompt | a placeholder to display when there is no active search, can refer to a localization string. |
| mode | defines the search strategy to either contain all words, any word or exact phrase. Supported options: all, any, exact. Default: all. |
| scope | specifies a model query scope method defined in the list model to apply to the search query. The first argument will contain the query object (as per a regular scope method), the second will contain the search term, and the third will be an array of the columns to be searched. |
| searchOnEnter | setting this to true will make the search widget wait for the Enter key to be pressed before it starts searching (the default behavior is that it starts searching automatically after someone enters something into the search field and then pauses for a short moment). Default: false. |
上述提及的工具栏按钮局部应包含工具栏控件定义和一些按钮。该局部还可以包含一个记分板控件以及图表。一个工具栏局部的示例,其中包含新建文章按钮,该按钮引用了由表单行为提供的创建操作:
<div data-control="toolbar">
<a href="<?= Backend::url('acme/blog/posts/create') ?>"
class="btn btn-primary oc-icon-plus">
New Post
</a>
</div>当使用列表复选框时,你可以使用 data-list-checked-trigger 属性来切换按钮的启用状态。
<button
type="button"
class="btn btn-primary"
data-list-checked-trigger>
Delete Selected
</button>你还可以使用 data-list-checked-request 属性将选中的值传递给 AJAX 请求。
<button
type="button"
class="btn btn-primary"
data-request="onDelete"
data-list-checked-request>
Delete Selected
</button>要根据用户定义输入筛选列表,请将以下列表配置添加到 YAML 文件中:
filter: $/acme/blog/models/post/scopes.yaml该 过滤器 属性应引用一个 过滤器配置文件 路径或提供一个包含配置的数组.
可用的列表列属性可以在 列表列定义 页面找到。
列表列通过 YAML 文件定义。列配置由列表行为用于创建记录表并在表格单元格中显示模型列。该文件放置在插件的 models 目录的子目录中。子目录名称与小写的模型类名匹配。文件名无关紧要,但 columns.yaml 和 list_columns.yaml 是常用名称。列表列文件位置示例:
├── plugins
| └── acme
| └── blog
| └── models
| ├── post ← 配置目录
| | └── columns.yaml ← 配置文件
| └── Post.php ← 模型类
下一个示例显示了列表列定义文件的典型内容。
# columns.yaml
columns:
name: Name
email: Email通常列表显示在 索引视图 文件中。由于列表包含工具栏, 视图文件将仅包含 单个 listRender 方法调用。
<?= $this->listRender() ?>列表行为可以使用命名定义来支持同一控制器中的多个列表。$listConfig 属性可以定义为一个数组,其中键是定义名称,值是配置文件。
public $listConfig = [
'templates' => 'config_templates_list.yaml',
'layouts' => 'config_layouts_list.yaml'
];每个定义即可通过在调用 listRender 方法时将定义名称作为第一个参数传入来显示。
<?= $this->listRender('templates') ?>有时你可能希望修改默认的列表行为,有几种方法可以实现。
您可以使用 listGetConfig 方法动态地扩展列表配置。
public function listGetConfig($definition)
{
$config = $this->asExtension('ListController')->listGetConfig($definition);
// Implement structure dynamically
$config->structure = [
'showTree' => true
];
return $config;
}您可以在控制器中使用您自己的逻辑来实现 index 动作方法,然后选择性地调用 List 行为的 index 父方法。
public function index()
{
//
// Do any custom code here
//
// Call the ListController behavior index() method
$this->asExtension('ListController')->index();
}ListController行为具有一个主容器视图,您可以通过在控制器目录中创建一个名为_list_container.php的特殊文件来覆盖该视图。以下示例将为列表添加一个侧边栏:
<?php if ($toolbar): ?>
<?= $toolbar->render() ?>
<?php endif ?>
<?php if ($filter): ?>
<?= $filter->render() ?>
<?php endif ?>
<div class="row row-flush">
<div class="col-sm-3">
[Insert sidebar here]
</div>
<div class="col-sm-9 list-with-sidebar">
<?= $list->render() ?>
</div>
</div>此行为将调用一个 Lists 小部件,其中也包含您可以覆盖的众多视图。这可以通过指定一个 customViewPath 属性来实现,如列表配置选项中所述。该小部件将首先在此路径中查找视图,然后回退到默认位置。
# Custom view path
customViewPath: $/acme/blog/controllers/reviews/list建议使用一个子目录,例如命名为
list,以避免冲突。
例如,要修改列表主体行标记,创建一个名为list/_list_body_row.php的文件,在你的控制器目录中。
<tr>
<?php foreach ($columns as $key => $column): ?>
<td><?= $this->getColumnValue($record, $column) ?></td>
<?php endforeach ?>
</tr>您可以通过绑定到 backend.list.extendColumns 全局事件 从外部扩展另一个控制器的列。该事件函数将接受一个 $list 参数,该参数表示 Backend\Widgets\Lists 对象,您可以使用其 getController 和 getModel 方法来检查执行上下文。
由于此事件可能影响所有列表,因此务必检查控制器和模型是否为正确类型。以下是一个示例,说明如何使用 addColumns 方法将新列添加到事件日志列表,并修改现有列。
Event::listen('backend.list.extendColumns', function($list) {
if (
!$list->getController() instanceof \System\Controllers\EventLogs ||
!$list->getModel() instanceof \System\Models\EventLog
) {
return;
}
// Add a new column
$list->addColumns([
'my_column' => [
'label' => 'My Column'
]
]);
// Modify an existing column
$list->getColumn('title')->useConfig([
'path' => 'column_title'
]);
});您也可以通过重写控制器类中的 listExtendColumns 方法,在内部扩展列表列。这只会影响由 ListController 行为所使用的列表。
class Categories extends \Backend\Classes\Controller
{
public $implement = [
\Backend\Behaviors\ListController::class
];
public function listExtendColumns($list)
{
$list->addColumns([...]);
$list->getColumn(...);
}
}以下方法可在 $list 对象上使用。
| Method | Description |
|---|---|
| addColumns | adds new columns to the list |
| removeColumn | removes a column from the list |
| getColumn | returns an existing column definition |
每个方法都接受一个列数组类似于列表列配置。
您可以通过在控制器类上添加一个 listInjectRowClass 方法来注入自定义 CSS 行类。此方法可以接受两个参数,$record 将代表一个模型记录,而 $definition 包含列表部件定义的名称。您可以返回任何包含您的行类的字符串值。这些类将被添加到行的 HTML 标记中。
class Lessons extends \Backend\Classes\Controller
{
// ...
public function listInjectRowClass($lesson, $definition = null)
{
// Strike through past lessons
if ($lesson->lesson_date->lt(Carbon::today())) {
return 'strike';
}
}
}一个特殊的CSS类 nolink 可用于强制行不可点击,即使 recordUrl 或 recordOnClick 属性已为列表小部件定义。在事件中返回此类将允许您使记录不可点击 - 例如,用于软删除的行或用于信息性行:
public function listInjectRowClass($record, $value)
{
if ($record->trashed()) {
return 'nolink';
}
}您可以指定列记录的点击操作通过重写 listOverrideRecordUrl 方法。此方法可以返回一个用于新的后端 URL 的字符串或一个具有复杂定义的数组。
public function listOverrideRecordUrl($record, $definition = null)
{
if ($record->is_active) {
return "acme/test/services/preview/{$record->id}";
}
}要覆盖 onclick 行为 返回一个包含 onclick 键的数组 并将 url 更改为 null。
public function listOverrideRecordUrl($record, $definition = null)
{
if ($record->is_banned) {
return ['onclick' => "alert('Unable to click')", 'url' => null];
}
}要使列完全不可点击,请返回一个数组,其中 clickable 键设置为 false。
public function listOverrideRecordUrl($record, $definition = null)
{
if ($record->is_disabled) {
return ['clickable' => false];
}
}您可以扩展另一个控制器的筛选范围,通过绑定到 backend.filter.extendScopes 全局事件。此方法可以接受参数 $filter,它将代表 Backend\Widgets\Filter 对象,您可以在其中使用 getController,getModel 和 getContext 方法来检查执行上下文。
由于此事件可能会影响所有过滤器,因此必须检查控制器和模型的类型是否正确。这是一个使用 addScopes 方法的示例,用于将新字段添加到事件日志列表,并调整 CSS 类。
Event::listen('backend.filter.extendScopes', function($filter) {
if (
!$filter->getController() instanceof \System\Controllers\EventLogs ||
!$filter->getModel() instanceof \System\Models\EventLog
) {
return;
}
// Add a new scope
$filter->addScopes([
'my_scope' => [
'label' => 'My Filter Scope'
]
]);
// Add custom CSS classes to the filter widget
$filter->cssClasses = array_merge(
$filter->cssClasses,
['my-array', 'of-classes']
);
});您也可以在控制器类内部扩展过滤器作用域,只需重写 listFilterExtendScopes 方法即可。
class Categories extends \Backend\Classes\Controller
{
public $implement = [
\Backend\Behaviors\ListController::class
];
public function listFilterExtendScopes($filter)
{
$filter->addScopes([...]);
}
}以下方法在$filter对象上可用。可用的作用域与列表过滤器配置相同。
| Method | Description |
|---|---|
| addScopes | adds new scopes to filter widget using list filters configuration |
| removeScope | remove scope from filter widget |
| getScope | returns an existing scope definition |
listExtendRefreshResults 方法可以在列表更新时与 AJAX 更新响应交互,并应返回一个包含额外部分更新的数组。listGetFilterWidget 将返回过滤器小部件,以便访问作用域。
public function listExtendRefreshResults($filter, $result)
{
$statusCode = $this->listGetFilterWidget()->getScope('status_code')->value;
return ['#my-partial-id' => $this->makePartial(...)];
}列表的查找查询 数据库模型 可以通过覆盖控制器类中的 listExtendQuery 方法进行扩展。本示例将确保软删除的记录包含在列表数据中,通过将 withTrashed 作用域应用到查询。
public function listExtendQuery($query)
{
$query->withTrashed();
}当处理同一个控制器中的多个列表定义时,您可以使用listExtendQuery的第二个参数,该参数包含定义的名称。
public $listConfig = [
'inbox' => 'config_inbox_list.yaml',
'trashed' => 'config_trashed_list.yaml'
];
public function listExtendQuery($query, $definition)
{
if ($definition === 'trashed') {
$query->onlyTrashed();
}
}您还可以联接其他表以协助搜索和排序。以下将联接 post_statuses 表并引入 status_sort_order 和 status_name 列到查询中。
public function listExtendQuery($query, $definition = null)
{
$query->leftJoin('post_statuses', 'posts.status_id', 'post_statuses.id');
$query->addSelect(
'post_statuses.sort_order as status_sort_order',
'post_statuses.name as status_name'
);
}该列表筛选器 模型查询也可以通过覆盖 listFilterExtendQuery 方法进行扩展。
public function listFilterExtendQuery($query, $scope)
{
if ($scope->scopeName == 'status') {
$query->where('status', `<>`, 'all');
}
}列表使用的记录集合可以通过覆盖控制器类中的 listExtendRecords 方法来扩展。此示例使用 记录集合 上的 sort 方法来更改记录的排序顺序。
public function listExtendRecords($records)
{
return $records->sort(function ($a, $b) {
return $a->computedVal() > $b->computedVal();
});
}自定义列表列类型可以在后端通过 插件注册文件 的 registerListColumnTypes 方法注册。该方法应返回一个数组,其中键是类型名称,值是可调用函数。可调用函数接收三个参数,原生 $value,$column 定义对象和模型 $record 对象。
public function registerListColumnTypes()
{
return [
// A local method, i.e $this->evalUppercaseListColumn()
'uppercase' => [$this, 'evalUppercaseListColumn'],
// Using an inline closure
'loveit' => function($value) { return "I love {$value}"; }
];
}
public function evalUppercaseListColumn($value, $column, $record)
{
return strtoupper($value);
}使用自定义列表列类型就像使用 type 属性按名称调用它一样简单。
# columns.yaml
columns:
secret_code:
label: Secret code
type: uppercase