it-swarm.com.de

Wie filtere ich Benutzer auf der Admin-Benutzerseite nach benutzerdefiniertem Metafeld?

Das Problem

WP scheint den Wert meiner Abfragevariablen zu entfernen, bevor er zum Filtern der Benutzerliste verwendet wird.

Mein Code

Diese Funktion fügt meiner Users-Tabelle in /wp-admin/users.php eine benutzerdefinierte Spalte hinzu:

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

Diese Funktion teilt WP mit, wie die Werte in der Spalte gefüllt werden sollen:

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

Dies fügt ein Dropdown-Menü und die Schaltfläche Filter über der Tabelle Users hinzu:

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

Diese Funktion ändert die Benutzerabfrage, um meinen meta_query hinzuzufügen:

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

Andere Informationen

Es erstellt meine Dropdown richtig. Wenn ich einen Kursabschnitt auswähle und auf Filter klicke, wird die Seite aktualisiert und course_section wird in der URL angezeigt, aber es ist kein Wert zugeordnet. Wenn ich die HTTP-Anforderungen überprüfe, wird angezeigt, dass sie mit dem korrekten Variablenwert gesendet wurden, aber dann gibt es einen 302 Redirect, der den von mir ausgewählten Wert zu entfernen scheint.

Wenn ich die Variable course_section durch direkte Eingabe in die URL übermittle, funktioniert der Filter wie erwartet.

Mein Code basiert ungefähr auf diesem Code von Dave Court .

Ich habe auch versucht, meine Abfragevariable mit diesem Code auf die Positivliste zu setzen, aber ohne Erfolg:

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

Ich verwende WP 4.4. Irgendwelche Ideen, warum mein Filter nicht funktioniert?

9
morphatic

UPDATE 2018-06-28

Während der folgende Code meistens gut funktioniert, ist hier eine Umschreibung des Codes für WP> = 4.6.0 (unter Verwendung von 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');

Ich habe einige Ideen von @birgire und @cale_b aufgenommen, die auch Lösungen anbieten, die es wert sind, gelesen zu werden. Insbesondere ich:

  1. Verwendete die Variable $which, die in v4.6.0 hinzugefügt wurde
  2. Verwendete Best Practice für i18n unter Verwendung übersetzbarer Zeichenfolgen, z. __( 'Filter' )
  3. Ausgetauschte Schleifen für die (modischeren?) array_map(), array_filter() und range()
  4. Wird sprintf() zum Generieren der Markup-Vorlagen verwendet
  5. Verwendete die eckige Klammer-Array-Notation anstelle von array()

Zuletzt entdeckte ich einen Fehler in meinen früheren Lösungen. Diese Lösungen bevorzugen immer den TOP <select> gegenüber dem BOTTOM <select>. Wenn Sie also eine Filteroption aus der oberen Dropdown-Liste ausgewählt und anschließend eine aus der unteren Dropdown-Liste ausgewählt haben, verwendet der Filter immer noch nur den Wert, der oben oben angegeben wurde (sofern er nicht leer ist). Diese neue Version behebt diesen Fehler.

UPDATE 2018-02-14

Dieses Problem wurde behoben seit WP 4.6.0 und die Änderungen sind in den offiziellen Dokumenten dokumentiert . Die unten stehende Lösung funktioniert jedoch immer noch.

Was hat das Problem verursacht (WP <4.6.0)

Das Problem bestand darin, dass die Aktion restrict_manage_users zweimal aufgerufen wurde: einmal ÜBER der Tabelle Users und einmal UNTER dieser. Dies bedeutet, dass ZWEI select Dropdowns mit dem gleichen Namen erstellt werden. Wenn auf die Schaltfläche Filter geklickt wird, überschreibt der Wert im zweiten Element select (d. H. Unter der Tabelle) den Wert im ersten Element, d. H. Den Wert über der Tabelle.

Wenn Sie in die Quelle WP eintauchen möchten, wird die Aktion restrict_manage_users innerhalb von WP_Users_List_Table::extra_tablenav($which) ausgelöst. Diese Funktion erstellt das systemeigene Dropdown-Menü, um die Rolle eines Benutzers zu ändern. Diese Funktion verfügt über die $which-Variable, die angibt, ob der select über oder unter dem Formular erstellt wird, und die es ermöglicht, den beiden Dropdowns unterschiedliche name-Attribute zuzuweisen. Leider wird die Variable $which nicht an die Aktion restrict_manage_users übergeben, daher müssen wir uns eine andere Methode ausdenken, um unsere eigenen benutzerdefinierten Elemente zu unterscheiden.

Eine Möglichkeit, dies zu tun, besteht, wie @Linnea oben vorschlägt, darin, JavaScript hinzuzufügen, um den Klick Filter abzufangen und die Werte der beiden Dropdowns zu synchronisieren. Ich entschied mich für eine reine PHP-Lösung, die ich jetzt beschreiben werde.

Wie man es repariert

Sie können die Möglichkeit nutzen, HTML-Eingaben in Felder mit Werten umzuwandeln und das Feld dann zu filtern, um nicht definierte Werte zu entfernen. Hier ist der Code:

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

Bonus: PHP 7 Refactor

Da ich von PHP 7 begeistert bin, ist hier für den Fall, dass Sie WP auf einem PHP 7-Server ausführen, eine kürzere, aufreizendere Version mit der Option null Vereinigungsoperator ?? :

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

Genießen!

6
morphatic

Ich habe Ihren Code sowohl in Wordpress 4.4 als auch in Wordpress 4.3.1 getestet. Mit Version 4.4 stoße ich genau auf dasselbe Problem wie Sie. Ihr Code funktioniert jedoch in Version 4.3.1 korrekt!

Ich denke, das ist ein Wordpress-Bug. Ich weiß nicht, ob es schon gemeldet wurde. Ich denke, der Grund für den Fehler könnte sein, dass die Senden-Schaltfläche die Abfrage-Variablen zweimal sendet. Wenn Sie sich die Abfragevariablen ansehen, werden Sie feststellen, dass course_section zweimal aufgeführt wird, einmal mit dem richtigen Wert und einmal leer.

Bearbeiten: Dies ist die JavaScript-Lösung

Fügen Sie dies einfach zur Datei functions.php Ihres Themas hinzu und ändern Sie NAME_OF_YOUR_INPUT_FIELD in den Namen Ihres Eingabefelds! Da WordPress jQuery automatisch auf der Administratorseite lädt, müssen Sie keine Skripte in die Warteschlange stellen. Dieser Codeausschnitt fügt den Dropdown-Eingaben einfach einen Änderungslistener hinzu und aktualisiert dann die andere Dropdown-Liste automatisch so, dass sie mit demselben Wert übereinstimmt. Mehr Erklärung hier.

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

Hoffe das hilft!

4
Linnea Huxford

Im Kern sind die bottom eingegebenen Namen mit der Instanznummer gekennzeichnet, z. new_role (oben) und new_role2 (unten). Hier sind zwei Ansätze für eine ähnliche Namenskonvention, nämlich course_section1 (oben) und course_section2 (unten):

Ansatz Nr. 1

Da die Variable $which ( top , bottom ) nicht an den Hook restrict_manage_users übergeben wird, können wir dies umgehen, indem wir eine eigene Version dieses Hooks erstellen:

Erstellen wir den Aktions-Hook wpse_restrict_manage_users, der Zugriff auf eine $which-Variable hat:

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

} );

Dann können wir es haken mit:

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

    // your stuff here
} );

wo wir jetzt $name als course_section1 am top und course_section2 am bottom haben.

Ansatz 2

Lassen Sie uns restrict_manage_users aufrufen, um Dropdowns mit einem anderen Namen für jede Instanz anzuzeigen:

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

wo wir die Kernfunktion selected() und die Hilfsfunktion benutzt haben:

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

Dies können wir auch verwenden, wenn wir im Aktionsrückruf pre_get_users nach dem ausgewählten Kursabschnitt suchen.

4
birgire

Dies ist eine andere Javascript-Lösung, die für einige Leute hilfreich sein kann. In meinem Fall habe ich einfach die 2. (untere) Auswahlliste komplett entfernt. Ich finde, dass ich die unteren Eingänge sowieso nie benutze ...

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
locomo

Nicht-JavaScript-Lösung

Geben Sie dem select einen Namen, der "array-style" ist, wie folgt:

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

Dann werden BEIDE Parameter übergeben (von oben und unten in der Tabelle) und jetzt in einem bekannten Array-Format.

Dann kann der Wert wie folgt in der Funktion pre_get_users verwendet werden:

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

eine andere Lösung

sie können Ihr Filterauswahlfeld in eine separate Datei wie user_list_filter.php einfügen.

und benutze require_once 'user_list_filter.php' in deiner Action Callback Funktion

user_list_filter.php Datei:

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

und in Ihrer Aktion Rückruf:

function add_course_section_filter() {
    require_once 'user_list_filter.php';
}
0
Alpha Elf