codekanzlei / cake-list-filter
列表过滤器组件/助手,用于轻松创建CRUD列表
Requires
- php: ~7.1
- cakephp/cakephp: >=3.0
- cakephp/plugin-installer: *
Requires (Dev)
- cakephp/cakephp-codesniffer: dev-master
README
这是一个 CakePHP 3 插件,它提供了一种轻松创建 CRUD 列表过滤器的简单方法。插件将动态渲染必要的标记,并动态操作控制器的 $paginate
配置。
安装
composer require "codekanzlei/cake-list-filter": "dev-master"
在你的应用的 config/bootstrap.php
中加载插件
Plugin::load('ListFilter', ['bootstrap' => false, 'routes' => false]);
在你的 AppController 中,或者你想要使用 ListFilter 的控制器中,加载 ListFilterComponent
和 ListFilterHelper
。
public $helpers = [
'ListFilter.ListFilter'
];
public $components = [
'ListFilter.ListFilter'
];
用法
你可以在发生分页的控制器中直接定义可过滤的字段。这确保了只能使用你配置的字段进行搜索,这也自动生成了必要的表单。
你可以将字段定义为控制器中的 $listFilter
属性,或者实现一个 getListFilters() 回调方法。
在单个控制器中可以存在多个 listFilters,因为配置是根据控制器动作分离的。在这个例子中,我们假设你希望在控制器动作 index
中有一个 ListFilter。
public $listFilters = [
'index' => [
'fields' => [
'Posts.title' => [
'searchType' => 'wildcard',
'inputOptions' => [
'label' => 'Post Title'
]
]
]
]
];
通过回调方法配置 ListFilters 提供了更多的灵活性,因为它允许执行在类属性中不可能执行的代码。
public function getListFilters($action) {
$filters = [];
if ($action == 'index') {
$filters = [
'fields' => [
'Posts.title' => [
'searchType' => 'wildcard',
'inputOptions' => [
'label' => __('posts.title')
]
],
'Posts.author_id' => [
'searchType' => 'select',
'options' => $this->Posts->Authors->find('list'),
'inputOptions' => [
'label' => __('posts.author')
]
]
]
];
}
return $filters;
}
我们假设 index
控制器动作看起来像这样
public function index()
{
$this->paginate['contain'] = ['Authors'];
$posts = $this->paginate($this->Posts);
$this->set('posts', $posts);
}
现在,在你的 index.ctp
视图文件中,你可以这样渲染过滤器框
<?= $this->ListFilter->renderFilterbox() ?>
你的过滤器框将看起来像这样
选项
searchType
这可以是以下之一
wildcard
:执行带有给定字符串的 LIKE 搜索select
:渲染包含options
配置键选项的下拉列表。只能使用此数组配置键来过滤,因此不可能进行 URL 操作。multipleselect
:与 select 类似,但渲染一个允许多选的下拉列表。fulltext
:将给定搜索词通过空格分割,并确保所有这些词都通过 LIKE 存在于字段中。searchFields
:如果指定此数组,将搜索多个字段termsCallback
:一个回调函数,它接收包含搜索词的数组并必须返回一个数组。可以用来影响搜索逻辑。
inputOptions
这些选项将用于使用 FormHelper::input()
渲染表单字段。因此,在这里,你可以设置标签、向输入添加类等。
searchTermsConjunction
设置组件时,你可以提供一个额外的选项数组来覆盖默认配置。特别是 searchTermsConjunction,默认为 'AND',意味着在全文搜索中多个术语将以 AND 连接添加到分页条件中,以缩小搜索结果。
如果你希望通过 OR 连接术语来扩大结果,你可以这样设置组件
public $components = [
'ListFilter.ListFilter' => [
'searchTermsConjunction' => 'OR'
]
];
处理多对多关系
为了处理多对多关系,例如 Users BelongsToMany Groups,你必须构建一个自定义查询并将其传递给分页器,因为分页器默认不能处理多对多关系。
public function index()
{
$this->paginate['contain'] = ['Users'];
$query = $this->Users->query();
if (isset($this->paginate['conditions']['Users.group_id'])) {
$groupId = $this->paginate['conditions']['Users.group_id'];
unset($this->paginate['conditions']['Users.group_id']);
$query->matching('Groups', function ($q) use ($groupId) {
return $q->where(['Group.id' => $groupId]);
});
}
$users = $this->paginate($query);
$this->set(compact('users'));
}
自定义
如果您需要为filterbox创建自定义布局,您可以按照自己的意愿单独构建filter box。listFilter中的每个元素都可以单独渲染
<?= $this->ListFilter->openForm(); ?>
<?= $this->ListFilter->openContainer(); ?>
<div class="row">
<div class="col-md-4">
<?= $this->ListFilter->filterWidget('Projects.user_id'); ?>
</div>
<div class="col-md-4">
<?= $this->ListFilter->filterWidget('Objects.customer_id'); ?>
</div>
<div class="col-md-4">
<?= $this->ListFilter->filterWidget('Projects.status'); ?>
</div>
</div>
<?= $this->ListFilter->filterButton(); ?>
<?= $this->ListFilter->resetButton(); ?>
<?= $this->ListFilter->closeContainer(); ?>
<?= $this->ListFilter->filterWidget('Projects.fulltext_search', [
'inputOptions' => [
'label' => false,
'placeholder' => __('projects.search'),
'prepend' => '<i class="fa fa-search"></i>'
]
]) ?>
<?= $this->ListFilter->closeForm(false, false); ?>
此外,您可以通过调用ListFilterHelper::config()并使用您的覆盖来操作默认的模板和类。以下是可用的选项
protected $_defaultConfig = [
'formOptions' => [],
'includeJavascript' => true,
'templates' => [
'containerStart' => '<div{{attrs}}>',
'containerEnd' => '</div>',
'toggleButton' => '<a{{attrs}}><i class="fa fa-plus"></i></a>',
'header' => '<div class="panel-heading">{{title}}<div class="pull-right">{{toggleButton}}</div></div>',
'contentStart' => '<div{{attrs}}>',
'contentEnd' => '</div>',
'buttons' => '<div class="submit-group">{{buttons}}</div>'
],
'containerClasses' => 'panel panel-default list-filter',
'contentClasses' => 'panel-body',
'toggleButtonClasses' => 'btn btn-xs toggle-btn',
'title' => 'Filter',
'filterButtonOptions' => [
'div' => false,
'class' => 'btn btn-xs btn-primary'
],
'resetButtonOptions' => [
'class' => 'btn btn-default btn-xs'
]
];