Yönetici kullanıcıları sayfasındaki kullanıcıları özel meta alana göre nasıl filtreleyebilirim?


9

Sorun

WP, kullanıcı listesini filtrelemek için kullanılmadan önce sorgu değişkenimin değerini kaldırmış gibi görünüyor.

Kodum

Bu işlev aşağıdakilerdeki Kullanıcılar tabloma özel bir sütun ekler /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' );

Bu işlev WP'ye sütundaki değerleri nasıl dolduracağını bildirir:

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

Bu Filter, Kullanıcılar tablosunun üzerine bir açılır menü ve düğme ekler :

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

Bu işlev, kullanıcı sorgusunu eklemek için kullanıcı sorgusunu değiştirir 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' );

Diğer bilgiler

Açılışımı doğru bir şekilde oluşturur. Bir kurs bölümü seçtiğimde ve URL'yi tıkladığında Filtersayfanın yenilenip course_sectiongösterildiğini, ancak bununla ilişkili bir değeri olmadığında. HTTP isteklerini kontrol edersem, doğru değişken değeri ile gönderildiğini gösterir, ancak daha sonra 302 Redirectseçtiğim değeri çıkarmış gibi görünüyor.

course_sectionDeğişkeni doğrudan URL'ye yazarak gönderirsem, filtre beklendiği gibi çalışır.

Kodum kabaca Dave Court'un bu koduna dayanıyor .

Ben de bu kodu kullanarak benim sorgu var beyaz liste çalıştı, ama hiçbir şans ile:

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

WP 4.4 kullanıyorum. Filtremin neden çalışmadığına dair bir fikrin var mı?


FYI, WP Trac sitesine , geliştiricilerin aşağıda açıklanan çemberlerden herhangi birini atlamak zorunda kalmamaları için bir bilet ekledim .
Morphatic

Yanıtlar:


6

GÜNCELLEME 2018-06-28

Aşağıdaki kod çoğunlukla iyi çalışıyor olsa da, WP> = 4.6.0 (PHP 7 kullanarak) kodunun yeniden yazılması:

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

Aşağıda okumaya değer çözümler sunan @birgire ve @cale_b'den birkaç fikir ekledim. Özellikle, ben:

  1. $whichEklenen değişken kullanıldıv4.6.0
  2. Çevrilebilir dizeler kullanarak i18n için en iyi uygulama kullanılır, örn. __( 'Filter' )
  3. İçin döngüler alışverişinde (daha şık?) array_map(), array_filter()Verange()
  4. sprintf()İşaretleme şablonlarını oluşturmak için kullanılır
  5. Yerine köşeli ayraç dizi gösterimi kullanıldı array()

Son olarak, önceki çözümlerimde bir hata buldum. Bu çözümler her zaman <select>ÜSTÜN ÜSTÜNÜ tercih eder <select>. Bu nedenle, üst açılır menüden bir filtre seçeneği belirledikten sonra alt açılır menüden bir filtre seçeneği belirlediyseniz, filtre yine de yalnızca en üstte olan değeri (boş değilse) kullanır. Bu yeni sürüm bu hatayı düzeltir.

GÜNCELLEME 2018-02-14

Bu sorun WP 4.6.0'dan beri düzeltilmiştir ve değişiklikler resmi belgelerde belgelenmiştir . Yine de aşağıdaki çözüm hala işe yarıyor.

Problemin Sebebi (WP <4.6.0)

Sorun, restrict_manage_userseylemin iki kez çağrılmasıydı: bir kez Kullanıcılar tablosunun üstünde ve bir kez aşağıda. Bu select, aynı adla İKİ açılır listenin oluşturulduğu anlamına gelir . Zaman Filterdüğmesi tıklatıldığında, herhangi bir değer ikinci bir select(örneğin aşağıda Tablo biri) örneğin, tablo yukarıda, birinci değeri geçersiz kılar elemanı.

WP kaynağına dalmak isterseniz, restrict_manage_userseylem içeriden tetiklenir WP_Users_List_Table::extra_tablenav($which); bu, kullanıcının rolünü değiştirmek için yerel açılır listeyi oluşturan işlevdir. Bu işlev, formun üstünü $whichmü yoksa altını mı oluşturduğunu söyleyen değişkenin yardımına sahiptir selectve iki açılır listeye farklı nameözellikler vermesine izin verir . Ne yazık ki, $whichdeğişken restrict_manage_userseyleme geçmez , bu nedenle kendi özel öğelerimizi farklılaştırmanın başka bir yolunu bulmalıyız.

@Linnea'nın önerdiği gibi , bunu yapmanın bir yolu , Filtertıklamayı yakalamak ve iki açılır listenin değerlerini senkronize etmek için biraz JavaScript eklemek olacaktır . Şimdi anlatacağım sadece PHP çözümünü seçtim.

Nasıl düzeltilir?

HTML girişlerini değer dizilerine dönüştürme özelliğinden faydalanabilir ve ardından tanımlanmamış değerlerden kurtulmak için diziyi filtreleyebilirsiniz. İşte kod:

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

Bonus: PHP 7 Refactor

PHP 7 hakkında heyecanlandığım için, bir PHP 7 sunucusunda WP çalıştırıyorsanız, burada boş birleştirme operatörünü?? kullanan daha kısa, daha seksi bir sürüm :

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

Zevk almak!


Peki çözümünüz 4.6.0'dan sonra hala çalışıyor mu? Wordpress'in en yeni sürümü ile bunu yapmanın daha kolay bir yolu var mı? Bu yılki gibi bir rehber bulamıyorum
Jeremy Muckel

1
@JeremyMuckel sorunuzun kısa cevabı "evet". Eski çözümüm hala çalışıyor. Aylardır düzenli olarak üretimde kullanıyorum ve sitelerimin çoğu en son kararlı WP sürümüne (şu anda 4.9.6) güncellendi. Bununla birlikte, yeni yamayı kullanan ve önceki çözümümdeki küçük bir hatayı da düzelten güncellenmiş bir çözüm sağladım.
Morphatic

Bu yardımcı oldu ama "Nasıl düzeltebilirim" ve "Bonus: PHP 7 Refactor" altında form kodu eksik bir </select>ben de işe almak için buldum <form method="get">seçim menüsünden önce ve </form>sonra bir filtre düğmesi koymak zorunda kaldı .
cogdog

@cogdog eksik </select>etiketleri iyi yakala ! Onları ekledim. <form>Bu sayfanın tamamını büyük bir formda sarıldığından ve bu kodun ortasına enjekte edildiğinden beri sarmanız gerektiği garip . Yine de çalışmasına sevindim. :)
morfatik

4

Çekirdekte, alt giriş adları örnek numarası ile işaretlenmiştir, örneğin new_role(üst) ve new_role2(alt). Benzer bir adlandırma kuralı için iki yaklaşım vardır: course_section1(üst) ve course_section2(alt):

Yaklaşım # 1

Yana $whichdeğişken ( üst , alt geçirilen almaz) restrict_manage_userskanca, biz bu kancanın kendi versiyonunu yaratarak bu çevrede alabilir:

wpse_restrict_manage_usersBir $whichdeğişkene erişimi olan eylem kancasını oluşturalım :

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

} );

Sonra ile kanca yapabiliriz:

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

    // your stuff here
} );

Nerede şimdi var $nameolarak course_section1en üst ve course_section2en alt .

Yaklaşım # 2

İçine edelim kanca restrict_manage_users, her örneği için farklı bir isimle, açılan menü görüntülemek için:

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

burada çekirdek işlevi selected()ve yardımcı işlevi kullandık:

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

Ardından, pre_get_userseylem geri çağrısında seçilen ders bölümünü kontrol ettiğimizde de kullanabiliriz .


Bu büyüleyici bir yaklaşım. staticAnahtar kelimeyi hiç bu şekilde kullanmadım (sadece sınıflar içinde). Does $instanceBunu yaparken küresel bir değişkeni haline? Değişken adı çarpışmaları hakkında endişelenmeniz mi gerekiyor? Ayrıca, domuzcukların mevcut bir eylemi destekleyen yeni bir eylem yaratma tekniğini de seviyorum. Teşekkürler!
Morphatic

Bu yaklaşım bazen kullanışlı olabilir ve çekirdekte örneğin kısa kod (galeri, çalma listesi, ses) örneklerini saymak için kullanılır. Buradaki statik değişken kapsamı, global değişken kapsamıyla uğraşmayacaktır. Statik değişkenin değeri, yerel değişkenler için geçerli olmayan bu işlev çağrıları arasında korunur. Daha fazla ayrıntı içeren bu güzel öğreticiyi aradım ve buldum . @morphatic
birgire

4

Kodunuzu hem Wordpress 4.4 hem de Wordpress 4.3.1'de test ettim. 4.4 sürümünde, sizinle tam olarak aynı sorunla karşılaşıyorum. Ancak, kodunuz 4.3.1 sürümünde düzgün çalışıyor!

Bence bu bir Wordpress hatası. Henüz raporlanıp raporlanmadığını bilmiyorum. Hatanın arkasındaki nedeni, gönderme düğmesinin sorguyu göndermesinin iki kez değişebileceğini düşünüyorum. Sorgu değişkenlerine bakarsanız, course_section öğesinin bir kez doğru değerle ve bir kez boş olarak iki kez listelendiğini göreceksiniz.

Düzenleme: Bu JavaScript Çözümü

Bunu temanızın function.php dosyasına ekleyin ve NAME_OF_YOUR_INPUT_FIELD öğesini giriş alanınızın adıyla değiştirin! WordPress, yönetici tarafında otomatik olarak jQuery yüklediğinden, herhangi bir komut dosyasını sıralamanız gerekmez. Bu kod snippet'i, açılır girişlere bir değişiklik dinleyicisi ekler ve ardından diğer açılır listeyi aynı değere uyacak şekilde otomatik olarak günceller. Daha fazla açıklama burada.

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

Bu yardımcı olur umarım!


Teşekkürler Linnea. Evet, aynı şeyi buldum, tıkladığınızda Filterdoğru değeri gönderir, ancak daha sonra tekrar sayfaya yönlendirir, bu sefer değeri sıyırır. Benim tahminim, rastgele, potansiyel olarak kötü niyetli, değerlerin gönderilmesini önlemek için bir çeşit güvenlik "özelliği" olmasıdır, ancak nasıl çalışacağımı bilmiyorum. İç çekmek.
Morphatic

OH! Var'ın neden iki kez ortaya çıktığını anladım. Çünkü ÜST ve AŞAĞIDAKİ kullanıcı tablosunda bir açılır menü olduğu için her ikisi de aynı nameözelliğe sahiptir. Filtrelemeyi yapmak için tablonun altındaki açılır menüyü kullanırsam, beklendiği gibi çalışır. Bu alan, üstündeki alandan sonra geldiğinden, null değeri öncekini geçersiz kılar. Hmmm ....
morfatik

İyi bulmak! Yinelemenin nereden geldiğini anlamaya çalışıyordum. Bence belki biraz JavaScript bunu düzeltebilir. Formu göndermeden önce diğer açılır listeyi aynı değerle ayarlamasını sağlayın.
Linnea Huxford

1

Bu, bazı insanlar için yararlı olabilecek farklı bir Javascript çözümüdür. Benim durumumda 2. (alt) seçim listesini tamamen kaldırdım. Zaten alt girişleri asla kullanmadığımı görüyorum ...

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 Dışı Çözüm

Seçime "dizi tarzı" bir ad verin, şöyle:

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

Daha sonra BOTH parametreleri (tablonun üst ve botomundan) ve şimdi bilinen bir dizi formatında geçirilir.

Daha sonra, değer fonksiyonda şu şekilde kullanılabilir 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

başka bir çözüm

filtre seçim kutunuzu aşağıdaki gibi ayrı bir dosyaya koyabilirsiniz user_list_filter.php

ve require_once 'user_list_filter.php'eylem geri arama işlevinizde kullanın

user_list_filter.php dosya:

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

ve eylem geri aramanızda:

function add_course_section_filter() {
    require_once 'user_list_filter.php';
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.