Làm cách nào để lọc người dùng trên trang người dùng quản trị theo trường meta tùy chỉnh?


9

Vấn đề

WP xuất hiện để loại bỏ giá trị của biến truy vấn của tôi trước khi nó được sử dụng để lọc danh sách người dùng.

Mã của tôi

Hàm này thêm một cột tùy chỉnh vào bảng Người dùng của tôi trên /wp-admin/users.php:

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' );

Hàm này cho WP biết cách điền giá trị vào cột:

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' );

Điều này thêm một menu thả xuống và Filternút phía trên bảng Người dùng:

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' );

Hàm này thay đổi truy vấn người dùng để thêm 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' );

Thông tin khác

Nó tạo ra thả xuống của tôi một cách chính xác. Khi tôi chọn một phần khóa học và nhấp vào Filtertrang sẽ làm mới và course_sectionhiển thị trong URL, nhưng nó không có giá trị liên quan đến nó. Nếu tôi kiểm tra các yêu cầu HTTP, nó cho thấy rằng nó được gửi với giá trị biến chính xác, nhưng sau đó có một 302 Redirectthứ dường như loại bỏ giá trị tôi đã chọn.

Nếu tôi gửi course_sectionbiến bằng cách nhập trực tiếp vào URL, bộ lọc sẽ hoạt động như mong đợi.

Mã của tôi đại khái dựa trên mã này từ Dave Court .

Tôi cũng đã thử danh sách trắng truy vấn var của mình bằng mã này, nhưng không có may mắn:

function add_course_section_query_var( $qvars ) {
    $qvars[] = 'course_section';
    return $qvars;
}
add_filter( 'query_vars', 'add_course_section_query_var' );

Tôi đang sử dụng WP 4.4. Bất kỳ ý tưởng tại sao bộ lọc của tôi không hoạt động?


FYI, tôi đã thêm một vé trên trang WP Trac để ngăn các nhà phát triển phải nhảy qua bất kỳ vòng đua nào được mô tả dưới đây.
thái

Câu trả lời:


6

CẬP NHẬT 2018-06-28

Mặc dù mã bên dưới hầu hết hoạt động tốt, đây là cách viết lại mã cho WP> = 4.6.0 (sử dụng 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');

Tôi đã kết hợp một số ý tưởng từ @birgire và @cale_b, những người cũng cung cấp các giải pháp dưới đây đáng để đọc. Cụ thể, tôi:

  1. Đã sử dụng $whichbiến được thêm vàov4.6.0
  2. Được sử dụng thực tiễn tốt nhất cho i18n bằng cách sử dụng các chuỗi có thể dịch, ví dụ: __( 'Filter' )
  3. Trao đổi vòng cho (thời trang hơn?) array_map(), array_filter()range()
  4. Được sử dụng sprintf()để tạo các mẫu đánh dấu
  5. Đã sử dụng ký hiệu mảng vuông thay vì array()

Cuối cùng, tôi phát hiện ra một lỗi trong các giải pháp trước đây của tôi. Những giải pháp đó luôn ưu tiên TOP <select>trên BOTTOM <select>. Vì vậy, nếu bạn đã chọn tùy chọn bộ lọc từ danh sách thả xuống trên cùng, sau đó chọn một tùy chọn từ danh sách thả xuống dưới cùng, bộ lọc sẽ vẫn chỉ sử dụng bất kỳ giá trị nào ở trên cùng (nếu nó không trống). Phiên bản mới này sửa lỗi đó.

CẬP NHẬT 2018/02/14

Đây vấn đề đã được vá từ WP 4.6.0những thay đổi được diễn tả trong các tài liệu chính thức . Các giải pháp dưới đây vẫn hoạt động, mặc dù.

Điều gì gây ra vấn đề (WP <4.6.0)

Vấn đề là restrict_manage_usershành động được gọi hai lần: một lần TRÊN bảng Người dùng và một lần DƯỚI ĐÂY. Điều này có nghĩa là các selectdanh sách thả xuống HAI được tạo với cùng tên . Khi Filternhấp vào nút, bất kỳ giá trị nào nằm trong selectphần tử thứ hai (tức là phần DƯỚI bảng) sẽ ghi đè giá trị trong phần đầu tiên, tức là phần TRÊN bảng.

Trong trường hợp bạn muốn đi sâu vào nguồn WP, restrict_manage_usershành động được kích hoạt từ bên trong WP_Users_List_Table::extra_tablenav($which), đó là chức năng tạo ra trình đơn thả xuống gốc để thay đổi vai trò của người dùng. Hàm đó có sự trợ giúp của $whichbiến cho nó biết liệu nó đang tạo selectbiểu mẫu trên hay dưới biểu mẫu và cho phép nó đưa ra hai namethuộc tính khác nhau . Thật không may, $whichbiến không được chuyển sang restrict_manage_usershành động, vì vậy chúng tôi phải đưa ra một cách khác để phân biệt các yếu tố tùy chỉnh của riêng chúng tôi.

Một cách để làm điều này, như @Linnea gợi ý , sẽ là thêm một số JavaScript để bắt Filternhấp và đồng bộ hóa các giá trị của hai danh sách thả xuống. Tôi đã chọn một giải pháp chỉ dành cho PHP mà tôi sẽ mô tả ngay bây giờ.

Cách khắc phục

Bạn có thể tận dụng khả năng biến các đầu vào HTML thành các mảng giá trị và sau đó lọc mảng để loại bỏ mọi giá trị không xác định. Đây là mã:

    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 '</select>';
        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' );

Phần thưởng: Tái cấu trúc PHP 7

Vì tôi rất hào hứng với PHP 7, trong trường hợp bạn đang chạy WP trên máy chủ PHP 7, đây là phiên bản ngắn hơn, hấp dẫn hơn bằng cách sử dụng toán tử hợp nhất 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 '</select>';
    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' );

Thưởng thức!


Vì vậy, giải pháp của bạn vẫn hoạt động sau 4.6.0? Có cách nào dễ dàng hơn để làm điều đó với phiên bản mới nhất của wordpress không? Tôi dường như không thể tìm thấy bất kỳ hướng dẫn nào được thực hiện trong năm nay
Jeremy Muckel

1
@JeremyMuckel câu trả lời ngắn cho câu hỏi của bạn là "có." Giải pháp cũ của tôi vẫn hoạt động. Tôi đã sử dụng nó trong sản xuất thường xuyên trong nhiều tháng nay và hầu hết các trang web của tôi được cập nhật lên phiên bản WP ổn định mới nhất (hiện tại là 4.9.6). Điều đó đang được nói, tôi đã cung cấp một giải pháp cập nhật sử dụng bản vá mới và cũng sửa một lỗi tinh vi trong giải pháp trước đây của tôi.
biến hình

Điều này rất hữu ích nhưng mã biểu mẫu của bạn trong phần "Cách khắc phục" và "Phần thưởng: Trình tái cấu trúc PHP 7" bị thiếu </select>Tôi cũng tìm thấy để nó hoạt động, tôi phải đặt <form method="get">trước menu chọn và </form>sau nút lọc.
cogdog

@cogdog bắt tốt trên các </select>thẻ còn thiếu ! Tôi đã thêm chúng. Điều kỳ lạ là bạn cần phải bọc nó <form>vì toàn bộ trang này được bọc trong một hình thức lớn và mã này được đưa vào giữa nó. Vui vì bạn đã làm cho nó làm việc, mặc dù. :)
thái

4

Trong lõi, các tên đầu vào dưới cùng được đánh dấu bằng số hiệu, ví dụ new_role(trên cùng) và new_role2(dưới cùng). Dưới đây là hai cách tiếp cận cho một quy ước đặt tên tương tự, cụ thể là course_section1(trên cùng) và course_section2(dưới cùng):

Cách tiếp cận số 1

$whichbiến số ( trên cùng , dưới cùng ) không được truyền vào restrict_manage_usershook, chúng ta có thể khắc phục điều đó bằng cách tạo phiên bản của hook đó:

Hãy tạo hook hành động wpse_restrict_manage_userscó quyền truy cập vào một $whichbiến:

add_action( 'restrict_manage_users', function() 
{
    static $instance = 0;   
    do_action( 'wpse_restrict_manage_users', 1 === ++$instance ? 'top' : 'bottom'  );

} );

Sau đó chúng ta có thể nối nó với:

add_action( 'wpse_restrict_manage_users', function( $which )
{
    $name = 'top' === $which ? 'course_section1' : 'course_section2';

    // your stuff here
} );

nơi bây giờ chúng tôi có $namenhư course_section1đầucourse_section2phía dưới .

Cách tiếp cận số 2

Hãy nối vào restrict_manage_users, để hiển thị danh sách thả xuống, với một tên khác nhau cho mỗi trường hợp:

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' );

trong đó chúng ta đã sử dụng hàm lõi selected()và hàm trợ giúp:

/**
 * 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;
}

Sau đó, chúng tôi cũng có thể sử dụng điều này khi chúng tôi kiểm tra phần khóa học đã chọn trong cuộc pre_get_usersgọi lại hành động.


Đây là một cách tiếp cận hấp dẫn. Tôi chưa bao giờ sử dụng statictừ khóa theo cách này (chỉ trong các lớp học). Có $instancetrở thành một biến toàn cầu khi bạn làm điều này? Bạn có phải lo lắng về sự va chạm tên biến? Tôi cũng thích kỹ thuật tạo ra một hành động mới dựa trên hành động hiện có. Cảm ơn!
thái

Cách tiếp cận này đôi khi có thể có ích và được sử dụng trong lõi để ví dụ đếm các trường hợp shortcode (bộ sưu tập, danh sách phát, âm thanh). Phạm vi biến tĩnh ở đây sẽ không gây rối với phạm vi biến toàn cục. Giá trị của biến tĩnh sẽ được bảo toàn giữa các lệnh gọi hàm đó, không phải là trường hợp với các biến cục bộ. Tôi đã tìm kiếm và tìm thấy hướng dẫn tốt đẹp này có nhiều chi tiết hơn. @ biến hình
birgire

4

Tôi đã kiểm tra mã của bạn trong cả Wordpress 4.4 và trong Wordpress 4.3.1. Với phiên bản 4.4, tôi gặp phải vấn đề chính xác như bạn. Tuy nhiên, mã của bạn hoạt động chính xác trong phiên bản 4.3.1!

Tôi nghĩ rằng đây là một lỗi Wordpress. Tôi không biết nếu nó đã được báo cáo chưa. Tôi nghĩ lý do đằng sau lỗi có thể là nút gửi đang gửi các vars truy vấn hai lần. Nếu bạn nhìn vào các vars truy vấn, bạn sẽ thấy khóa học được liệt kê hai lần, một lần với giá trị chính xác và một lần trống.

Chỉnh sửa: Đây là Giải pháp JavaScript

Chỉ cần thêm tệp này vào tệp tin.php của chủ đề và thay đổi NAME_OF_YOUR_INPUT_FIELD thành tên của trường nhập của bạn! Vì WordPress tự động tải jQuery về phía quản trị viên, bạn không phải tranh thủ bất kỳ tập lệnh nào. Đoạn mã này chỉ cần thêm một trình nghe thay đổi vào các đầu vào thả xuống và sau đó tự động cập nhật danh sách thả xuống khác để khớp với cùng một giá trị. Giải thích thêm ở đây.

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
} );

Hi vọng điêu nay co ich!


Cảm ơn Linnea. Vâng, tôi đã tìm thấy một điều tương tự, khi bạn nhấp vào Filternó sẽ gửi giá trị chính xác, nhưng sau đó chuyển hướng trở lại trang một lần nữa, lần này tước bỏ giá trị. Tôi đoán rằng đó là một số "tính năng" bảo mật để ngăn chặn các giá trị ngẫu nhiên, có khả năng gây hại, được gửi, nhưng tôi không biết cách xử lý xung quanh nó. Thở dài.
thái

OH! Tôi đã tìm ra lý do tại sao var xuất hiện hai lần. Bởi vì có một danh sách thả xuống cả TRÊN và DƯỚI bảng người dùng và cả hai đều có cùng namethuộc tính. Nếu tôi sử dụng danh sách thả xuống DƯỚI bảng để thực hiện quá trình lọc, nó sẽ hoạt động như mong đợi. Vì trường đó xuất hiện sau trường bên trên, nên giá trị null sẽ ghi đè lên trường trước đó. Hmmm ....
morphatic

Tìm tốt Tôi đã cố gắng tìm ra nơi trùng lặp đến từ đâu. Tôi nghĩ có lẽ một chút JavaScript có thể khắc phục điều này. Đặt nó thả xuống khác làm cùng một giá trị trước khi gửi biểu mẫu.
Linnea Huxford

1

Đây là một giải pháp Javascript khác nhau có thể hữu ích cho một số người. Trong trường hợp của tôi, tôi chỉ cần loại bỏ hoàn toàn danh sách chọn thứ 2 (dưới cùng). Tôi thấy rằng tôi không bao giờ sử dụng các đầu vào dưới cùng ...

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
} );

1

Giải pháp không phải JavaScript

Đặt tên chọn là "kiểu mảng", như vậy:

echo '<select name="course_section[]" style="float:none;">';

Sau đó, các tham số BOTH được truyền (từ đỉnh và botom của bảng), và bây giờ ở định dạng mảng đã biết.

Sau đó, giá trị có thể được sử dụng như thế này trong pre_get_usershàm:

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 );
}

0

giải pháp khác

bạn có thể đặt hộp chọn bộ lọc của mình vào tệp riêng biệt như user_list_filter.php

và sử dụng require_once 'user_list_filter.php'trong chức năng gọi lại hành động của bạn

user_list_filter.php tập tin:

<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="">

và trong cuộc gọi lại hành động của bạn:

function add_course_section_filter() {
    require_once 'user_list_filter.php';
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.