表格(table)一直是后台展示数据的利器,在WordPress中,文章、评论等就是以表格的形式展示的。它有一个专门用于生成展示表格的类,名称为WP_Table_List,位于wp-admin/includes/下面。针对不同情况,继承自它适当的覆盖某些方法即可以满足各种需求。下面根据一个需求简要说说如何使用,详细的请查看官方文档。
这次是弄一个插件,做的一个调查问卷,不过这个调查问卷有点特殊——科技美学那岩的四大旗舰盲评投票。最新一季第六季已经开始投票,但电脑网页版体验不够友好(其实要看出差别也是电脑看比较好),很多网友也在吐槽。我看到时那岩网站是用的WordPress搭建,故想着我能不能自己做出这么一个功能来,于是,就有了这回事——搞一个WordPress插件,功能是创建编辑盲评、展示并收集答卷,最后进行简单统计。
其实之前就弄过一般情况的调查问卷,要出选择、填空等多种类型的题工作量大不少,这里简单一点,只搞展示4张图片这一种题型。很容易的想到需要以下数据实体:
- 调查表(four_big),字段有这个调查的标题与描述等
- 一个调查的多个场景(four_big_group),字段有这个场景的标题与描述、展示顺序等
- 一个场景下的4张图片(four_big_group_item),这里一个调查下的4张图片,记录媒体ID即可,当然也有展示顺序
- 一个调查收集到的多个答卷(four_big_submit),主要字段是提交的IP,所属地区,提交时间等
- 一个答卷下各个场景的答案(four_big_submit_value),记录一个场景选择的是第几个
以上5个实体的增删改查,都是类似的,只是换一下表名,加多一个条件而已,因此非常适合使用WP_Table_List来展示。建表略,如何开始开发一个WordPress插件略,我们先创建一个名为Four_Big_Table的类,封装一些公用的属性方法,作为以上5个实体的基类。为了将必要的方法说全,本应放到子类的方法也一并写里边了。
首先,要继承自WP_Table_List,必须先将其引入。
if (!class_exists('WP_List_Table')) { require ( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' ); }
构造函数中,WP_Table_List接受几个参数,都是可选,其中特别说明一下screen这个,最好给一个值,否则容易导致一个screen的函数报一个变量未定义的Notice。具体看文档。
public function __construct($modName) { parent::__construct(array('screen' => 'four_big'));//不写screen容易报错一个Notice错误 //其他业务代码... }
展示表格之前需要准备总记录数、每页显示记录条数用于分页,当然当前页的数据是必不可少的。当前数据与总记录数是要查询数据库的,你可以分开写,但我喜欢写一块,因为它们的查询条件一样的。这里是随意命名,不是实现或覆盖父类的方法。
public function getDataAndTotal($per, $page, $where = '1') { $dataSql = "SELECT * FROM {$this->_table} WHERE $where"; $totalSql = "SELECT COUNT(*) FROM {$this->_table} WHERE $where"; if (!empty($_REQUEST['orderby'])) { $dataSql .= " ORDER BY ".esc_sql($_REQUEST['orderby']); $dataSql .= !empty($_REQUEST['order']) ? ' '.esc_sql($_REQUEST['order']) : ' ASC'; } else { switch ($this->mod) { case 'four_big_group': case 'four_big_group_item': $orderby = '`order` ASC'; break; case 'four_big': $orderby = 'ID DESC'; break; default: $orderby = 'ID DESC'; } $dataSql .= " ORDER BY $orderby"; } $dataSql .= " LIMIT $per"; $dataSql .= " OFFSET ".($page - 1) * $per; $data = $this->_db->get_results($dataSql, ARRAY_A); $total = $this->_db->get_var($totalSql); return array('data' => $data, 'total' => $total); }
以上_table是真实表名,mod是模型名,_db是数据库操作类$wpdb,这些根据当前业务自定义的。这里是定义一个比较通用的求当前数据与总记录数的方法。
要展示表格,当然要知道有哪些字段要展示,这里需要实现父类的一个方法get_columns(),返回一个数组。key是字段名,value是显示到表头的值。
public function get_columns() { $columns = array( 'cb' => '<input type="checkbox" />', 'title' => '标题', 'description' => '描述', 'create_time' => '创建时间', 'action' => '操作', ); return $columns; }
其中cb表示生成一个checkbox,这个需要批量操作就是固定的。有了字段就会循环当前页数据去展示每一行,每一行中每一个单元格td的值通常也需要处理一翻才显示出来,如整型的时间戳就需要通过date()函数格式化后再输出。但很多字段是直接原样输出的,因此,先实现一个父类里的column_default方法,表示默认的处理td数据方法。
public function column_default($item, $column_name) { switch ($column_name) { case 'title': case 'value': case 'ip': case 'agent': case 'address': case 'ID': case 'media_id': case 'order': return $item[$column_name]; case 'description': return mb_substr($item['description'], 0, 40); case 'create_time': return date('Y-m-d H:i:s', $item['create_time']); default: return "column_default中没有这个$column_name"; } }
以上,很多字段是直接输出的,只需简单处理的也直接写上了。至于特殊的字段,再定义一个名为“column_特殊字段名”的方法来处理。如上边图中“操作”这个action字段就是需要特殊处理。
public function column_action($item) { $actions = array( 'edit' => sprintf( '<a href="?page=%s&action=edit&ID=%s">编辑</a>', $_REQUEST['page'], $item['ID'] ), 'viewgroup' => sprintf( '<a href="?page=%s&mod=four_big_group&action=list&parent=%s">场景</a>', $_REQUEST['page'], $item['ID'] ), 'submit' => sprintf( '<a href="?page=%s&mod=four_big_submit&action=list&parent=%s">答卷</a>', $_REQUEST['page'], $item['ID'] ), 'view' => sprintf( '<a href="%s?ID=%s" target="_blank">预览</a>', site_url().'/fourbig', $item['ID'] ), 'delete' => sprintf( '<a href="?page=%s&action=delete&ID=%s&_wpnonce=%s">删除</a>', $_REQUEST['page'], $item['ID'], $this->createNonce('delete') ), ); return $this->row_actions($actions, true); }
row_actions()这个是父类中的方法,用于生成action操作按钮,就是把上边的几个a链接拼接一下。添加true参数表示一直显示。在WordPress中其实见得多的是为false的情况,鼠标在哪一行才显示,如下面的显示场景(four_big_group)的table:
展示了数据和分页,基本一个表格就完了。但都已经在每行前边加了checkbox选择框,肯定得有批量操作跟它对应,比如批量删除。要生成批量操作的下拉select,实现get_bulk_actions()方法即可。
public function get_bulk_actions() { $actions = array( 'bulk-delete' => '删除'//key-val,key是option的value,val是option的显示文本 ); return $actions; }
既然有了批量操作,就得响应处理这些批量操作。在WordPress中,一般是直接表单提交来处理,整个table得用一个form来包裹(渲染表格时不会自动生成),点击“应用”按钮就会提交上去了。表格上下两个批量操作选择select的name是action和action2。删除有批量操作POST过来的批量删除和点击链接的单个删除,一并处理。这里自定义一个处理函数process_bulk_action():
public function process_bulk_action() { $action = $this->current_action(); if ($action === 'delete') { $nonce = esc_attr($_REQUEST['_wpnonce']); if (!$this->verifyNonce($nonce, 'delete')) { wp_die('error nonce'); } $ID = trim($_REQUEST['ID']); if (!ctype_digit($ID)) { wp_die('error ID'); } $result = $this->deleteByID($ID); $this->msg(sprintf($result . '个项目被删除。 <a href="%s">返回列表</a>', $_SERVER['HTTP_REFERER'])); wp_die(); } elseif ($action === 'bulk-delete') { $ids = $_REQUEST['ID']; foreach ($ids as $ID) { $this->deleteByID($ID); } $this->msg(count($ids) . '个项目被删除。 '); } }
这里,单个删除是通过访问a链接,生成的删除链接也使用action参数来指定操作(这里我为delete),就可以使用父类的current_action来接收了(实际它是接收批量操作select的action和action2)。不太清楚的时候看具体生成代码及使用var_dump()大法即可。删除完毕后最好是直接跳转回列表,但WordPress这个插件页面中间输出的,前边已经输出了很多东西,无法header跳转了。因此对于单个删除是a链接的GET请求,直接die掉提示一下即可(URL变了,继续展示数据很奇怪,再说如果继续刷新会因参数不全而出错),批量删除的POST操作,可以不管继续取数据展示。msg()方法只是输出一段带HTML提示代码而已。
最后,就是生成展示表格了,需要实现父类中的prepare_items()方法。
public function prepare_items() { if (!empty($_REQUEST['parent'])) { //取当前页数据和总记录的条件,如场景有个条件是属于哪个问卷的。 $where = 'parent=' . $_REQUEST['parent']; } else { $where = '1'; } $this->process_bulk_action();//处理批量操作(包括单个删除) $columns = $this->get_columns();//可见的显示的字段,上边有说 $hidden = array();//隐藏的不可见的字段,用于后续ajax操作等 $sormod = $this->get_sortable_columns();//可以排序的字段,为可见字段中的部分 $primary = array('title');//主字段,就是加粗一点 $this->_column_headers = array($columns, $hidden, $sormod, $primary);//存储以上字段 $per = $this->get_items_per_page($this->mod . '_per_page', $this->per);//参数1是存储键,参数2是默认值 $page = $this->get_pagenum();//父类中的方法,获取当前页码 $dataAndTotal = $this->getDataAndTotal($per, $page, $where);//自定义的方法,获取当前页数据和总记录数 $this->set_pagination_args(array( 'total_items' => $dataAndTotal['total'], 'per_page' => $per, ));//父类方法,设置分页参数 $this->items = $dataAndTotal['data'];//设置当前页数据,准备完毕! }
其中,为_column_headers设置值,父类中有get_column_info()方法,但其实这个方法是不建议开发者使用的,一般手动设置。get_items_per_page()父类中的方法,一般WordPress后台页面顶端都有个“显示选项”的按钮,点一下会出现可以设置一些参数或者选择哪些显示,这个就是跟那个有关的,可以设置每一页显示多少条。这里没搞那就是取参数2的默认值。数据都准备完毕,就差输出表格了,调用父类中的display()方法即可。但有时在表格上边或者左边右边的还需要加一些提示啊按钮之类的,封装一个方法展示吧。
public function displayTable() { ?> <div class="wrap"> <div id="icon-users" class="icon32"><br/></div> <h2> <?php echo sprintf('【%s】下的场景列表', $this->parent['title']); echo sprintf('<a href="%s" class="page-title-action">添加</a>', $this->getActionURL('add')); echo sprintf('<a class="page-title-action" href="%s" style="float:right">返回</a>', $this->getBaseURL()); ?> </h2> <form method="post" action=""> <input type="hidden" name="page" value="<?php echo $_REQUEST['page']?>" /> <?php $this->display(); ?> </form> </div> <?php }
最后,获得类的实例,准备数据,展示即可。
$table = new Four_Big_Table('four_big'); $table->prepare_items(); $table->displayTable();
至于其他添加、编辑之类的,其实不属于Table范围了,可以在prepare_items()之前,定义相应方法处理即可。
参考链接:
https://codex.wordpress.org/Class_Reference/WP_List_Table
http://www.sitepoint.com/using-wp_list_table-to-create-wordpress-admin-tables/
One comment
Pingback: 四大旗舰盲评前端效果 | 小喵爱你