Ich habe Benutzer-Metadaten mit dem Namen achievements
festgelegt, bei denen es sich um ein Array handelt, das verschiedene numerische Werte enthält. Mit WP_User_Query
möchte ich die Liste nach einem bestimmten Schlüssel in diesem Array mit dem Namen points
sortieren.
Folgende:
$args = array(
'order' => 'DESC',
'orderby' => 'meta_value_num',
'meta_key' => 'achievements',
'number' => $no,
'offset' => $offset
);
... funktioniert, wenn achievements
ein numerischer Wert wäre. Wie ändere ich das $args
-Array so, dass es auf den Schlüssel points
innerhalb des Metadatenschlüssels achievements
abzielt?.
Das Ausführen einer print_r()
von achievements
für einen Benutzer gibt Folgendes zurück:
Array ( [x1] => 74 [x2] => 383 [x3] => 457 [x4] => 81 [points] => 301 [x5] => 382 [x6] => 12 )
Sie können die Werte mit den Metaschlüsseln separate user speichern:
x1, ..., x6, points
anstelle des Arrays:
achievements
um Ihre WP_User_Query()
-Verwendung zu vereinfachen und nicht mit serialisierten Arrays als Benutzer-Meta-Werten umgehen zu müssen.
Aber ich denke, du hast dieses Setup bereits in Betracht gezogen und wolltest es vermeiden ;-)
Ein Kompromiss wäre, nur die points
aus dem achievements
-Array in einen separaten Metaschlüssel zu verschieben.
Update:
Wenn die Anzahl der Benutzer relativ gering ist, können wir die folgende (nicht getestete) PHP Sortierung verwenden:
$users2points = array();
$args = array(
'meta_key' => 'achievements',
'number' => $no,
'offset' => $offset
);
$users = get_users( $args );
foreach( (array) $users as $user )
{
$meta = get_user_meta( 'achievements', $user->ID );
if( isset( $meta['points'] )
$users2points[$user->ID] = $meta['points'];
}
asort( $users2points, SORT_NUMERIC );
print_r( $users2points );
Update 2:
Hier sind noch weitere Abenteuer: PHP Sortierung, die direkt auf das $users
-Array von WP_User
-Objekten angewendet werden kann:
$args = array(
'meta_key' => 'achievements',
'number' => $no,
'offset' => $offset
);
$users = get_users( $args );
foreach( (array) $users as $user )
{
$meta = get_user_meta( 'achievements', $user->ID );
// Here we assume the points values are greater or equal to 0:
$user->points = ( isset( $meta['points'] ) ? $meta['points'] : 0;
}
/*
usort( $users, function( $a, $b ) {
return gmp_cmp( $a->points, $b->points );
});
*/
// The gmp_cmp() might suffer from a memory leak,
// according to some PHP bug reports,
// so let's use this instead:
usort( $users, function( $a, $b ) {
if( $a->points === $b->points )
return 0;
else
return ( $a->points > $b->points ) ? 1 : -1;
});
print_r( $users );
Eine Möglichkeit, dies mit SQL zu tun, besteht darin, SUBSTR
und LOCATE
zu verwenden, um den serialisierten Wert auszuwählen:
function wpse162668_pre_user_query( $query ) {
if ( ( $orderby = $query->get( 'orderby' ) ) != 'achievements_points' ) {
return $query;
}
global $wpdb;
$order = $query->get( 'order' );
$points_str = 's:6:"points";i:';
$query->query_fields .= $wpdb->prepare(
', SUBSTR(wp_usermeta.meta_value, LOCATE(%s, wp_usermeta.meta_value) + %d)+0 AS ' . $orderby,
$points_str, strlen( $points_str )
);
$query->query_orderby = 'ORDER BY ' . $orderby . ' ' . $order . ', user_login ASC';
return $query;
}
und ändere dann die $args
orderby
in 'orderby' => 'achievements_points'
und umhülle den get_users
mit dem Filter:
add_filter( 'pre_user_query', 'wpse162668_pre_user_query', 10, 2 );
$users = get_users( $args );
remove_filter( 'pre_user_query', 'wpse162668_pre_user_query', 10 );
Die Umwandlung in eine Ganzzahl (+0
) funktioniert, da MySQL nachfolgende nicht-numerische Elemente einfach ignoriert. Ich verwende diese Methode ziemlich oft (auf Postmeta, nicht auf Usermeta) in unterschiedlichen Kombinationen, da die Klasse WPAlchemy_MetaBox mit Wiederholungsgruppen verwendet wird, obwohl es sehr chaotisch werden kann.