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
変数を送信した場合、フィルタは期待どおりに機能します。
私のコードはおおよそ 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を使っています。私のフィルタが機能しない理由は何ですか?
以下のコードは大体うまくいきますが、WP> = 4.6.0(PHP 7を使う)のコードを書き換えます。
function add_course_section_filter( $which ) {
// create sprintf templates for <select> and <option>s
$st = '<select name="course_section_%s" style="float:none;"><option value="">%s</option>%s</select>';
$ot = '<option value="%s" %s>Section %s</option>';
// determine which filter button was clicked, if any and set section
$button = key( array_filter( $_GET, function($v) { return __( 'Filter' ) === $v; } ) );
$section = $_GET[ 'course_section_' . $button ] ?? -1;
// generate <option> and <select> code
$options = implode( '', array_map( function($i) use ( $ot, $section ) {
return sprintf( $ot, $i, selected( $i, $section, false ), $i );
}, range( 1, 3 ) ));
$select = sprintf( $st, $which, __( 'Course Section...' ), $options );
// output <select> and submit button
echo $select;
submit_button(__( 'Filter' ), null, $which, false);
}
add_action('restrict_manage_users', 'add_course_section_filter');
function filter_users_by_course_section($query)
{
global $pagenow;
if (is_admin() && 'users.php' == $pagenow) {
$button = key( array_filter( $_GET, function($v) { return __( 'Filter' ) === $v; } ) );
if ($section = $_GET[ 'course_section_' . $button ]) {
$meta_query = [['key' => 'courses','value' => $section, 'compare' => 'LIKE']];
$query->set('meta_key', 'courses');
$query->set('meta_query', $meta_query);
}
}
}
add_filter('pre_get_users', 'filter_users_by_course_section');
私は@birgireと@cale_bからいくつかのアイデアを取り入れました。それらもまた読む価値のある解決策を提供します。具体的には、
$which
に追加されたv4.6.0
変数を使用しました__( 'Filter' )
array_map()
、array_filter()
、range()
のループを交換しました。sprintf()
を使用しましたarray()
の代わりに角括弧配列表記を使用しました最後に、私は以前のソリューションでバグを発見しました。これらのソリューションは、常にBOTTOMの<select>
よりもTOPの<select>
を優先します。そのため、一番上のドロップダウンからフィルターオプションを選択し、次に一番下のドロップダウンからフィルターオプションを選択した場合でも、フィルターは一番上にある値のみを使用します(空白でない場合)。この新しいバージョンはそのバグを修正します。
この issueはWP 4.6.0 からパッチが適用され、 の変更点は公式文書 に文書化されています。しかし、以下の解決策はまだうまくいきます。
問題は、restrict_manage_users
アクションが2回呼び出されることです。1回はUsersテーブルの上に、もう1回はその下にあります。これは、2つのselect
ドロップダウンが 同じ名前 で作成されることを意味します。 Filter
ボタンがクリックされると、2番目のselect
要素の値(テーブルの下の値)が最初の要素の値、つまりテーブルの上の値をオーバーライドします。
WPソースに飛び込む場合は、 WP_Users_List_Table::extra_tablenav($which)
内からrestrict_manage_users
アクションがトリガーされます。これは、ユーザーの役割を変更するためのネイティブドロップダウンを作成する関数です。この関数は、フォームの上または下にselect
を作成しているかどうかを示し、2つのドロップダウンに異なるname
属性を指定できるようにする$which
変数の助けを借りています。残念ながら、$which
変数はrestrict_manage_users
アクションに渡されないため、独自のカスタム要素を区別する別の方法を考え出す必要があります。
@Linneaが上で示唆しているように、これを行うための1つの方法はFilter
クリックをキャッチして2つのドロップダウンの値を同期させるためにJavaScriptを追加することです。私はこれから説明するPHP専用のソリューションを選びました。
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に興奮しているので、WP 7をPHP 7サーバーで実行している場合は、ここで を使用したより短くセクシーなバージョンになります。 null合体演算子??
:
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' );
楽しい!
Wordpress 4.4とWordpress 4.3.1の両方でコードをテストしました。バージョン4.4では、あなたとまったく同じ問題に遭遇します。しかし、あなたのコードはバージョン4.3.1では正しく動作します。
これはWordpressのバグだと思います。まだ報告されているかどうかはわかりません。このバグの背後にある理由は、送信ボタンがクエリ変数を2回送信していることにあると思います。クエリvarsを見ると、course_sectionが2回リストされています。1回は正しい値で、もう1回は空です。
これをあなたのテーマのfunctions.phpファイルに追加し、NAME_OF_YOUR_INPUT_FIELDをあなたの入力フィールドの名前に変更するだけです! WordPressは自動的にjQueryを管理者側にロードするので、スクリプトをエンキューする必要はありません。このコードでは、ドロップダウン入力に変更リスナーを追加してから、もう一方のドロップダウンを同じ値に一致するように自動的に更新しています。 もっと説明がここにあります。
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
} );
お役に立てれば!
コアでは、bottom入力名はインスタンス番号でマークされています。 new_role
(上)およびnew_role2
(下)。同様の命名規則、つまりcourse_section1
(上)とcourse_section2
(下)の2つのアプローチを次に示します。
$which
変数(top、bottom)はrestrict_manage_users
フックに渡されないため、独自のバージョンのフックを作成することで回避できます。
wpse_restrict_manage_users
変数にアクセスできるアクションフック$which
を作成しましょう。
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
} );
topに$name
としてcourse_section1
があり、bottomにcourse_section2
があります)。
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
アクションコールバックで選択したコースセクションを確認するときにこれを使用することもできます。
これは別のJavascriptソリューションで、一部の人にとっては役に立つかもしれません。私の場合は、2番目(一番下)の選択リストを完全に削除しました。とにかく一番下の入力を使わないことに気づきました….
add_action( 'in_admin_footer', function() {
?>
<script type="text/javascript">
jQuery(".tablenav.bottom select[name='course_section']").remove();
jQuery(".tablenav.bottom input#post-query-submit").remove();
</script>
<?php
} );
次のように、「配列スタイル」の名前を選択します。
echo '<select name="course_section[]" style="float:none;">';
次に、両方のパラメータが(表の上と下から)渡され、既知の配列形式になります。
それから、値はpre_get_users
関数でこのように使われることができます:
function filter_users_by_course_section( $query ) {
global $pagenow;
// if not on users page in admin, get out
if ( ! is_admin() || 'users.php' != $pagenow ) {
return;
}
// if no section selected, get out
if ( empty( $_GET['course_section'] ) ) {
return;
}
// course_section is known to be set now, so load it
$section = $_GET['course_section'];
// the value is an array, and one of the two select boxes was likely
// not set to anything, so use array_filter to eliminate empty elements
$section = array_filter( $section );
// the value is still an array, so get the first value
$section = reset( $section );
// now the value is a single value, such as 1
$meta_query = array(
array(
'key' => 'course_section',
'value' => $section
)
);
$query->set( 'meta_key', 'course_section' );
$query->set( 'meta_query', $meta_query );
}
あなたはuser_list_filter.php
のような別のファイルにあなたのフィルター選択ボックスを置くことができます
アクションコールバック関数でrequire_once 'user_list_filter.php'
を使う
user_list_filter.php
ファイル:
<select name="course_section" style="float:none;">
<option value="">Course Section...</option>
<?php for ( $i = 1; $i <= 3; ++$i ) {
if ( $i == $_GET[ 'course_section' ] ) { ?>
<option value="<?=$i?>" selected="selected">Section <?=$i?></option>
<?php } else { ?>
<option value="<?=$i?>">Section <?=$i?></option>
<?php }
}?>
</select>
<input id="post-query-submit" type="submit" class="button" value="Filter" name="">
そしてあなたのアクションコールバックで:
function add_course_section_filter() {
require_once 'user_list_filter.php';
}