사용자 정의 메타 필드별로 관리자 페이지에서 사용자를 필터링하는 방법은 무엇입니까?


9

문제

WP는 사용자 목록을 필터링하는 데 사용되기 전에 쿼리 변수의 값을 제거하는 것으로 보입니다.

내 코드

이 함수는 다음의 사용자 테이블에 사용자 정의 열을 추가합니다 /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' );

이 함수는 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' );

그러면 FilterUsers 테이블 위에 드롭 다운과 버튼 이 추가됩니다 .

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

이 함수는 사용자 쿼리를 변경하여 my 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_sectionURL에 표시되지만 관련 값이 없습니다. HTTP 요청을 확인하면 올바른 변수 값으로 제출 된 것으로 표시되지만 선택한 값 302 Redirect을 제거하는 것으로 보입니다.

course_section변수를 URL에 직접 입력하여 제출하면 필터가 예상대로 작동합니다.

내 코드는 대략 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 Trac 사이트 에 개발자가 아래 설명 된 후프를 뛰어 넘지 못하게 하는 티켓을 추가했습니다 .
morphatic

답변:


6

업데이트 2018-06-28

아래 코드는 대부분 잘 작동하지만 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의 여러 아이디어를 통합하여 읽을 가치가있는 솔루션을 제공합니다. 구체적으로, 나는 :

  1. $which추가 된 변수를 사용 했습니다.v4.6.0
  2. 번역 가능한 문자열을 사용하여 i18n에 사용 된 모범 사례 (예 : __( 'Filter' )
  3. 에 대한 루프를 교환 (더 유행?) array_map(), array_filter()그리고range()
  4. sprintf()마크 업 템플릿 생성에 사용
  5. 대신 대괄호 배열 표기법을 사용했습니다. array()

마지막으로 이전 솔루션에서 버그를 발견했습니다. 이러한 솔루션은 항상 <select>BOTTOM보다 TOP 을 선호합니다 <select>. 따라서 맨 위 드롭 다운에서 필터 옵션을 선택한 다음 맨 아래 드롭 다운에서 하나를 선택하면 필터는 맨 위에 있던 값만 사용합니다 (공백이 아닌 경우). 이 새로운 버전은 그 버그를 수정합니다.

업데이트 2018-02-14

문제는 WP 4.6.0 이후 패치되었습니다변경 사항은 공식 문서에 설명되어 있습니다 . 아래 솔루션은 여전히 ​​작동합니다.

문제의 원인 (WP <4.6.0)

문제는 restrict_manage_users작업이 두 번 호출 되었다는 것입니다. 한 번은 사용자 테이블 위에, 한 번은 아래에 있습니다. 이는 두 개의 select드롭 다운이 동일한 이름으로 생성됨을 의미합니다 . 때 Filter버튼을 클릭하면, 어떤 값은 초이다 select(즉,하기 표 하나가) 즉, 테이블 위에 한 첫 번째의 요소 값을 대체.

WP 소스로 뛰어 들기를 원한다면 restrict_manage_users액션이 within WP_Users_List_Table::extra_tablenav($which)에서 트리거 되는데,이 기능은 사용자 역할을 변경하기위한 기본 드롭 다운을 생성하는 기능입니다. 이 함수는 $which변수가 select위 또는 아래 양식을 작성하는지 여부를 알려주 는 변수 를 사용하여 두 드롭 다운에 다른 name속성 을 제공 할 수 있습니다 . 불행히도 $which변수는 restrict_manage_users액션으로 전달되지 않으므로 사용자 정의 요소를 차별화하는 다른 방법을 찾아야 합니다.

@Linnea가 제안한 대로이 작업을 수행하는 한 가지 방법은 JavaScript를 추가하여 Filter클릭 을 잡고 두 드롭 다운 값을 동기화하는 것입니다. 지금 설명 할 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 '</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' );

보너스 : PHP 7 리 팩터

PHP 7에 대해 기쁘게 생각합니다. PHP 7 서버에서 WP를 실행하는 경우 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' );

즐겨!


4.6.0 이후에도 솔루션이 계속 작동합니까? 최신 버전의 워드 프레스로 더 쉬운 방법이 있습니까? 올해 같은 가이드를 찾을 수없는 것 같습니다
Jeremy Muckel

1
@JeremyMuckel 귀하의 질문에 대한 짧은 대답은 "예"입니다. 내 오래된 솔루션이 여전히 작동합니다. 나는 현재 몇 달 동안 프로덕션 환경에서 정기적으로 사용하고 있으며 대부분의 사이트는 안정적인 최신 WP 버전 (현재 4.9.6)으로 업데이트되었습니다. 즉, 새로운 패치를 사용하고 이전 솔루션의 미묘한 버그를 수정하는 업데이트 된 솔루션을 제공했습니다.
morphatic

이것은 도움이되었지만 "수정 방법"및 "보너스 : PHP 7 리 팩터"아래의 양식 코드가 누락되었습니다. </select>또한 <form method="get">선택 메뉴와 </form>필터 버튼 다음에 입력해야합니다.
cogdog

@cogdog 누락 된 </select>태그를 잘 잡으십시오 ! 나는 그들을 추가했다. <form>이 전체 페이지가 하나의 큰 형태로 싸여서이 코드가 중간에 삽입되기 때문에 랩으로 감싸 야하는 것이 이상 합니다. 그래도 작동하게되어 기쁩니다. :)
morphatic

4

코어에서 하단 입력 이름은 인스턴스 번호 (예 : new_role(위) 및 new_role2(아래))로 표시됩니다. 유사한 명명 규칙에 대한 두 가지 접근 방식, 즉 course_section1(위)와 course_section2(아래)가 있습니다.

접근법 # 1

때문에 $which변수 ( 상단 , 하단 에 전달되지 않습니다) 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
} );

여기서 우리가 지금 가지고있는 $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액션 콜백 에서 선택한 코스 섹션을 확인할 때도 사용할 수 있습니다 .


이것은 매혹적인 접근법입니다. 나는 static이런 식으로 키워드를 사용한 적이 없다 (클래스 내에서만). 이렇게 $instance하면 전역 변수가됩니까? 변수 이름 충돌에 대해 걱정해야합니까? 또한 기존 작업을 피기 백하는 새로운 작업을 만드는 기술이 마음에 듭니다. 감사!
morphatic

이 접근 방식은 때때로 편리 할 수 ​​있으며 핵심에서 단축 코드 (갤러리, 재생 목록, 오디오) 인스턴스 수를 계산하는 데 사용됩니다. 여기서 정적 변수 범위는 전역 변수 범위를 망칠 수 없습니다. 정적 변수의 값은 해당 함수 호출 사이에 유지되며 로컬 변수의 경우에는 해당되지 않습니다. 자세한 내용 이 포함 된 멋진 튜토리얼 을 검색하고 찾았습니다 . @morphatic
birgire

4

Wordpress 4.4 및 Wordpress 4.3.1에서 코드를 테스트했습니다. 버전 4.4에서는 당신과 똑같은 문제가 발생합니다. 그러나 코드가 버전 4.3.1에서 올바르게 작동합니다!

나는 이것이 Wordpress 버그라고 생각합니다. 아직보고되었는지는 모르겠습니다. 버그 뒤에있는 이유는 제출 버튼이 쿼리 변수를 두 번 전송하기 때문일 수 있습니다. 쿼리 변수를 보면 course_section이 올바른 값으로 한 번, 비어있는 한 번 두 번 표시됩니다.

편집 : 이것은 JavaScript 솔루션입니다

이것을 테마의 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
} );

도움이 되었기를 바랍니다!


고마워 Linnea. 예, 똑같은 것을 발견했습니다. 클릭 Filter하면 올바른 값을 제출하지만 다시 페이지로 다시 리디렉션됩니다. 이번에는 값이 제거됩니다. 내 생각에 임의의 잠재적으로 악의적 인 값이 제출되는 것을 막는 일종의 보안 "기능"이지만 해결 방법을 모르겠습니다. 한숨.
morphatic

오! var가 두 번 나타나는 이유를 알아 냈습니다. ABOVE 및 BELOW 드롭 다운이 있으므로 users 테이블과 둘 다 동일한 name속성을 갖습니다 . 필터링을 수행하기 위해 테이블 ​​아래 드롭 다운을 사용하면 예상대로 작동합니다. 해당 필드는 그 위의 필드 뒤에 오기 때문에 null 값이 이전 필드보다 우선합니다. 흠 ....
morphatic

잘 찾아라! 복제본이 어디서 왔는지 알아 내려고했습니다. 작은 JavaScript가 이것을 해결할 수 있다고 생각합니다. 양식을 제출하기 전에 다른 드롭 다운을 동일한 값으로 설정하십시오.
Linnea Huxford

1

이것은 일부 사람들에게 도움이 될 수있는 다른 Javascript 솔루션입니다. 제 경우에는 단순히 두 번째 (아래) 선택 목록을 완전히 제거했습니다. 어쨌든 하단 입력을 사용하지 않는다는 것을 알았습니다 ...

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

비 JavaScript 솔루션

선택에 다음과 같이 "배열 스타일"인 이름을 지정하십시오.

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

그런 다음 BOTH 매개 변수가 (테이블의 상단과 하단에서) 전달되고 이제 알려진 배열 형식으로 전달됩니다.

그런 다음 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 );
}

0

다른 해결책

필터 선택 상자를 별도의 파일에 넣을 수 있습니다. 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';
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.