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该filter属性应引用一个过滤器配置文件路径或提供一个包含配置的数组。
可用的列表列属性可在列表列定义页面上找到。
列表列通过 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'
);
}该 list filter 模型查询也可以通过覆盖 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