问题描述
问题
WP 用来删除我的查询变量的值,然后才能过滤用户列表。
我的代码
此功能将自定义列添加到/wp-admin/users.php
上的 Users 表中:
function add_course_section_to_user_meta( $columns ) {
$columns['course_section'] = 'Section';
return $columns;
}
add_filter( 'manage_users_columns', 'add_course_section_to_user_meta' );
该函数告诉 WP 如何填充列中的值:
function manage_users_course_section( $val, $col, $uid ) {
if ( 'course_section' === $col )
return get_the_author_meta( 'course_section', $uid );
}
add_filter( 'manage_users_custom_column', 'manage_users_course_section' );
这在 Users 表上面添加了一个下拉列表和 Filter
按钮:
function add_course_section_filter() {
echo '<select name="course_section" style="float:none;">';
echo '<option value="">Course Section...</option>';
for ( $i = 1; $i <= 3; ++$i ) {
if ( $i == $_GET[ 'course_section' ] ) {
echo '<option value="'.$i.'" selected="selected">Section '.$i.'</option>';
} else {
echo '<option value="'.$i.'">Section '.$i.'</option>';
}
}
echo '<input id="post-query-submit" type="submit" class="button" value="Filter" name="">';
}
add_action( 'restrict_manage_users', 'add_course_section_filter' );
此功能更改用户查询以添加我的 meta_query
:
function filter_users_by_course_section( $query ) {
global $pagenow;
if ( is_admin() &&
'users.php' == $pagenow &&
isset( $_GET[ 'course_section' ] ) &&
!empty( $_GET[ 'course_section' ] )
) {
$section = $_GET[ 'course_section' ];
$meta_query = array(
array(
'key' => 'course_section',
'value' => $section
)
);
$query->set( 'meta_key', 'course_section' );
$query->set( 'meta_query', $meta_query );
}
}
add_filter( 'pre_get_users', 'filter_users_by_course_section' );
其他信息
它正确地创建了我的下拉列表。当我选择课程部分并单击 Filter
时,页面刷新,course_section
显示在 URL 中,但没有与之相关联的值。如果我检查 HTTP 请求,它显示它提交了正确的变量值,但是后来有一个 302 Redirect
似乎删除了我选择的值。
如果我通过直接在 URL 中输入 course_section
变量,过滤器将按预期方式工作。
我的代码大致基于 this code from Dave Court 。
我也尝试使用这个代码列出我的查询 var,但是没有运气:
function add_course_section_query_var( $qvars ) {
$qvars[] = 'course_section';
return $qvars;
}
add_filter( 'query_vars', 'add_course_section_query_var' );
我正在使用 WP 4.4 。任何想法为什么我的过滤器不工作?
最佳解决方案
是什么导致的问题
问题是 restrict_manage_users
操作被调用了两次:一旦超过了 Users 表,并且一直是 BELOW 。这意味着使用相同的名称创建两个 select
下拉列表。当单击 Filter
按钮时,第二个 select
元素 (即表中的一个) 中的任何值都将覆盖第一个 Filter
元素中的值,即表中的值。
如果您想潜入 WP 源,restrict_manage_users
操作将从 WP_Users_List_Table::extra_tablenav($which)
中触发,WP_Users_List_Table::extra_tablenav($which)
操作是创建本机下拉菜单以更改用户角色的功能。该函数有 $which
变量的帮助,它可以告诉它是否在表单之上或之下创建 select
,并允许它给两个不同的 name
属性的下拉列表。不幸的是,$which
变量不会传递给 restrict_manage_users
动作,所以我们必须提出另一种方式来区分我们自己的自定义元素。
像 @Linnea 在上面建议的一种方法是添加一些 JavaScript 来捕获 Filter
点击并同步两个下拉列表的值。我选择了一个现在我将介绍的 PHP-only 解决方案。
如何修复
您可以利用将 HTML 输入转换为数组的能力,然后过滤数组以摆脱任何未定义的值。以下是代码:
function add_course_section_filter() {
if ( isset( $_GET[ 'course_section' ]) ) {
$section = $_GET[ 'course_section' ];
$section = !empty( $section[ 0 ] ) ? $section[ 0 ] : $section[ 1 ];
} else {
$section = -1;
}
echo ' <select name="course_section[]" style="float:none;"><option value="">Course Section...</option>';
for ( $i = 1; $i <= 3; ++$i ) {
$selected = $i == $section ? ' selected="selected"' : '';
echo '<option value="' . $i . '"' . $selected . '>Section ' . $i . '</option>';
}
echo '<input type="submit" class="button" value="Filter">';
}
add_action( 'restrict_manage_users', 'add_course_section_filter' );
function filter_users_by_course_section( $query ) {
global $pagenow;
if ( is_admin() &&
'users.php' == $pagenow &&
isset( $_GET[ 'course_section' ] ) &&
is_array( $_GET[ 'course_section' ] )
) {
$section = $_GET[ 'course_section' ];
$section = !empty( $section[ 0 ] ) ? $section[ 0 ] : $section[ 1 ];
$meta_query = array(
array(
'key' => 'course_section',
'value' => $section
)
);
$query->set( 'meta_key', 'course_section' );
$query->set( 'meta_query', $meta_query );
}
}
add_filter( 'pre_get_users', 'filter_users_by_course_section' );
奖金:PHP 7 重构
由于我对 PHP 7 感到兴奋,如果您在 PHP 7 服务器上运行 WP,则使用无法合并的运算符??
可以使用更短,更性感的版本:
function add_course_section_filter() {
$section = $_GET[ 'course_section' ][ 0 ] ?? $_GET[ 'course_section' ][ 1 ] ?? -1;
echo ' <select name="course_section[]" style="float:none;"><option value="">Course Section...</option>';
for ( $i = 1; $i <= 3; ++$i ) {
$selected = $i == $section ? ' selected="selected"' : '';
echo '<option value="' . $i . '"' . $selected . '>Section ' . $i . '</option>';
}
echo '<input type="submit" class="button" value="Filter">';
}
add_action( 'restrict_manage_users', 'add_course_section_filter' );
function filter_users_by_course_section( $query ) {
global $pagenow;
if ( is_admin() && 'users.php' == $pagenow) {
$section = $_GET[ 'course_section' ][ 0 ] ?? $_GET[ 'course_section' ][ 1 ] ?? null;
if ( null !== $section ) {
$meta_query = array(
array(
'key' => 'course_section',
'value' => $section
)
);
$query->set( 'meta_key', 'course_section' );
$query->set( 'meta_query', $meta_query );
}
}
}
add_filter( 'pre_get_users', 'filter_users_by_course_section' );
请享用!
次佳解决方案
在核心中,底部输入名称标有实例号,例如。 new_role
(上) 和 new_role2
(底部) 。以下是类似命名约定的两种方法,即 course_section1
(上) 和 course_section2
(底部):
方法#1
由于 $which
变量 (顶部,底部) 没有被传递给 restrict_manage_users
钩子,所以我们可以通过创建我们自己的该钩子的版本来解决这个问题:
我们创建一个可以访问 $which
变量的动作钩子 wpse_restrict_manage_users
:
add_action( 'restrict_manage_users', function()
{
static $instance = 0;
do_action( 'wpse_restrict_manage_users', 1 === ++$instance ? 'top' : 'bottom' );
} );
然后我们可以钩住它:
add_action( 'wpse_restrict_manage_users', function( $which )
{
$name = 'top' === $which ? 'course_section1' : 'course_section2';
// your stuff here
} );
我们现在有 $name
作为 course_section1
在顶部和 course_section2
在底部。
方法#2
让我们钩住 restrict_manage_users
,显示下拉列表,每个实例的名称不同:
function add_course_section_filter()
{
static $instance= 0;
// Dropdown options
$options = '';
foreach( range( 1, 3 ) as $rng )
{
$options = sprintf(
'<option value="%1$d" %2$s>Section %1$d</option>',
$rng,
selected( $rng, get_selected_course_section(), 0 )
);
}
// Display dropdown with a different name for each instance
printf(
'<select name="%s" style="float:none;"><option value="0">%s</option>%s</select>',
'course_section' . ++$instance,
__( 'Course Section...' ),
$options
);
// Button
printf (
'<input id="post-query-submit" type="submit" class="button" value="%s" name="">',
__( 'Filter' )
);
}
add_action( 'restrict_manage_users', 'add_course_section_filter' );
在那里我们使用了核心函数 selected()
和帮助函数:
/**
* Get the selected course section
* @return int $course_section
*/
function get_selected_course_section()
{
foreach( range( 1, 2) as $rng )
$course_section = ! empty( $_GET[ 'course_section' . $rng ] )
? $_GET[ 'course_section' . $rng ]
: -1; // default
return (int) $course_section;
}
那么当我们检查 pre_get_users
动作回调中选定的课程时,我们也可以使用这个。
第三种解决方案
我在 Wordpress 4.4 和 Wordpress 4.3.1 中测试了你的代码。与版本 4.4,我遇到完全一样的问题。但是,您的代码在 4.3.1 版中正常工作!
我认为这是一个 WordPress 的 bug 。我不知道是否还有报道。我认为错误背后的原因可能是提交按钮正在发送查询 vars 两次。如果查看查询 vars,您会看到 course_section 被列出两次,一次是正确的值,一次为空。
编辑:这是 JavaScript 解决方案
只需将其添加到主题的 functions.php 文件中,然后将 NAME_OF_YOUR_INPUT_FIELD 更改为输入字段的名称!由于 WordPress 会自动在管理端加载 jQuery,所以您不必排队任何脚本。这段代码只是将更改侦听器添加到下拉列表中,然后自动更新其他下拉列表以匹配相同的值。 More explanation here.
add_action( 'in_admin_footer', function() {
?>
<script type="text/javascript">
var el = jQuery("[name='NAME_OF_YOUR_INPUT_FIELD']");
el.change(function() {
el.val(jQuery(this).val());
});
</script>
<?php
} );
希望这可以帮助!
参考文献
注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。