You should really just forget about the first code in your post/question, but anyway, what you’re trying to achieve can be accomplished either by sorting at the database level or PHP level, and you would probably want to use the latter if pagination is not needed. But that is up to you to decide.
Option 1: Sorting at the database level.
This can be done using the CASE
operator in MySQL/MariaDB, along with a custom orderby
value, and the pre_user_query
hook to set/modify the ORDER BY
clause for your user query.
-
Define the query arguments:
$args = array( 'meta_query' => array( // Include users from the selected department (named $title) only, which // means only those who have the meta emp_dept. array( 'key' => 'emp_dept', 'value' => $title, 'compare' => 'LIKE', ), // Include both the users who have the emp_position meta, and those who do // not have that meta. array( 'relation' => 'OR', 'emp_position' => array( 'key' => 'emp_position', 'compare' => 'EXISTS', ), 'no_emp_position' => array( 'key' => 'emp_position', 'compare' => 'NOT EXISTS', ), ), ), 'orderby' => 'emp_position', );
-
Set/modify the
ORDER BY
clause via thepre_user_query
hook:add_action( 'pre_user_query', 'my_pre_user_query' ); function my_pre_user_query( \WP_User_Query $query ) { if ( 'emp_position' === $query->get( 'orderby' ) ) { $clauses = $query->meta_query->get_clauses(); // Get the table aliases used with the current query. If you changed the // array keys in your meta query, then change the ones below accordingly. $mt2 = $clauses['emp_position']['alias']; $mt3 = $clauses['no_emp_position']['alias']; // The WP_User_Query class does not (yet) have a FILTER hook for changing // the ORDER BY clause, but we can change it by direct modification on the // $query->query_orderby property. $query->query_orderby = <<<SQL ORDER BY # 1. Group the users. CASE WHEN ( $mt2.meta_value LIKE '%Manager%' ) THEN 1 WHEN ( $mt2.meta_value LIKE '%Supervisor%' ) THEN 2 WHEN ( $mt2.meta_value LIKE '%Foreman%' ) THEN 3 WHEN ( $mt2.meta_value LIKE '%Coordinator%' ) THEN 4 # Yes, these are intentionally 6 and then 5. WHEN ( $mt3.user_id IS NULL ) THEN 6 ELSE 5 END ASC, # 2. Sort the groups; A-Z. CAST( $mt2.meta_value AS CHAR ) ASC SQL; } }
Option 2: Sorting at the PHP level only.
This is just one of the many ways of doing it in PHP, but I hope this helps.
-
Define the query arguments – Just use the same
$args
as above, but omit theorderby
or don’t set it toemp_position
. -
Get and sort the users.
$users = get_users( $args ); $groups = array( 'Manager' => array(), 'Supervisor' => array(), 'Foreman' => array(), 'Coordinator' => array(), 'Other' => array(), ); // 1. Group the users. foreach ( $users as $i => $user ) { $group = 'Other'; if ( preg_match( '/(Manager|Supervisor|Foreman|Coordinator)/i', $user->emp_position, $matches ) ) { $group = ucfirst( $matches[1] ); } // Note that I'm just referencing and not copying the actual user object // stored in the $users array. $groups[ $group ][] = &$users[ $i ]; } $cmp = function ( $a, $b ) { // I used the ternary so that users having an empty emp_position are put // at the bottom. return $a->emp_position ? strcmp( $a->emp_position, $b->emp_position ) : 1; };
-
For completeness, this is how I displayed the results:
foreach ( $groups as $name => $list ) { echo '<h3>' . esc_html( $name ) . '</h3>'; // 2. Sort the group upon displaying; A-Z. usort( $list, $cmp ); echo '<ol>'; foreach ( $list as $user ) { printf( '<li>%s: <i>%s</i></li>', esc_html( $user->user_login ), esc_html( $user->emp_position ) ); } echo '</ol>'; }
Additionally, you should know that the 'position_clause' => 'DESC'
in your second code won’t work because the array did not have a direct key
item as in 'position_clause' => array( 'key' => 'emp_position' )
. =)