Get wordpress authors in order by the number of custom posts published

Viewed 605

In my WordPress v5.8.1, I have a list of authors who post in custom posts song and poem. With the below code I am getting the list of authors who posted in the both or either in one custom post.

 $authors = get_users(array(
        'who' => 'authors',
        'has_published_posts' => array('song','poem'),
        'orderby' => 'post_count',
        'order' => 'DESC',
        'number' => '15'
 ));

Below code is listing all the authors with their post counts:

foreach ($authors as $user) {
   $name = $user->first_name . ' ' . $user->last_name;
   $songs = count_user_posts($user->ID, $post_type = "song");
   $poems = count_user_posts($user->ID, $post_type = "poem");
   echo $name.' has '. $songs .' songs, and '. $poems .' poems;
}

With 'orderby' => 'post_count' in the arguments, I expected the authors list with highest combined custom posts count displayed first, however it is showing randomly with no order, neither by post_counts nor ID.

How can I order the authors with most combined total posts?

5 Answers

Try below code.

$authors = get_users(array(
    'who' => 'authors',
    'has_published_posts' => array('song','poem'),
    'orderby' => 'post_count',
    'order' => 'DESC',
    'number' => '15'
));

Create one array and count total poem + songs

$author_posts = array();

foreach ($authors as $user) {
    $name = $user->first_name . ' ' . $user->last_name;
    $songs = count_user_posts($user->ID, $post_type = "song");
    $poems = count_user_posts($user->ID, $post_type = "poem");
    $author_posts[] = array(
        'total' => $songs+$poems,
        'label' => $name.' has '. $songs .' songs, and '. $poems .' poems'
    );
}

now use `usort to sort an array by total.

usort($author_posts, function($a, $b) {
    if($a['total']==$b['total']) return 0;
    return $a['total'] < $b['total']?1:-1;
}); 

print the output.

foreach ( $author_posts as $key => $author_post ) {
    echo $author_post['label']."</br>";
}

Complete code.

$authors = get_users(array(
    'who' => 'authors',
    'has_published_posts' => array('song','poem'),
    'orderby' => 'post_count',
    'order' => 'DESC',
    'number' => '15'
));

$author_posts = array();

foreach ($authors as $user) {
    $name = $user->first_name . ' ' . $user->last_name;
    $songs = count_user_posts($user->ID, $post_type = "song");
    $poems = count_user_posts($user->ID, $post_type = "poem");
    $author_posts[] = array(
        'total' => $songs+$poems,
        'label' => $name.' has '. $songs .' songs, and '. $poems .' poems'
    );
}

usort($author_posts, function($a, $b) {
    if($a['total']==$b['total']) return 0;
    return $a['total'] < $b['total']?1:-1;
}); 

foreach ( $author_posts as $key => $author_post ) {
    echo $author_post['label']."</br>";
}

Tested and works.

First, the orderby argument of function get_users() works with post_count but counts all post types (here, it is counting poem, song and the others types like post or page).

If you want to order your results by specific custom post types, I would recommend to use the wpdb class to design your own request. This allows you to get the exact desired results with one single request and no need to use a foreach to sort your results after.

I tried this way and it worked:

$authors = $wpdb->get_results(
    "SELECT
        $wpdb->users.ID AS author_id,
        $wpdb->users.display_name AS author_name,
        COUNT($wpdb->posts.ID) AS published_songs_and_poems,
        COUNT(CASE WHEN $wpdb->posts.post_type = 'song' THEN $wpdb->posts.ID ELSE NULL END) as published_songs,
        COUNT(CASE WHEN $wpdb->posts.post_type = 'poem' THEN $wpdb->posts.ID ELSE NULL END) as published_poems
    FROM $wpdb->users
    JOIN $wpdb->posts
        ON $wpdb->posts.post_author = $wpdb->users.ID
        AND $wpdb->posts.post_type IN('song', 'poem') AND $wpdb->posts.post_status = 'publish'
    GROUP BY author_id
    ORDER BY published_songs_and_poems DESC"
);

This request return an object with results grouped by author and order by total amount of published songs and poems.

You can use this object as you already did. Something like this :

array_walk($authors, function($author) {
    echo $author->author_name." has published ".$author->published_songs." song(s) and ".$author->published_poems." poem(s).<br/>";
});

I edited this answer because the first version focused on how to return the results order by total post_count. This was clarified when commented by @theKing.

You have an error inside your foreach block, it might be what's causing the issue. Use this instead:

foreach ($authors as $user) {
   $name = $user->first_name . ' ' . $user->last_name;
   $songs = count_user_posts($user->ID, "songs");
   echo $name.' has '. $songs .' songs ';
}

In short, replace the $post_type = "songs" with simply "songs". See the documentation for details on accepted parameters.

EDIT: I misunderstood the question. Looking at your query, everything is as it should be so I would look elsewhere for the source of the problem. Do you have plugins or filters that might change the query? Can you reproduce your query directly in the database and does it work there? (to rule out table or index corruption)

Try this one:

<?php wp_list_authors( $args ); ?> 



<?php $args = array(
'post_type'     => 'custom_post_type',
'orderby'       => 'post_count', 
'order'         => 'DESC', 
'number'        => null,
'optioncount'   => false, 
'exclude_admin' => true, 
'show_fullname' => false,
'hide_empty'    => true,
'echo'          => true,
'style'         => 'list',
'html'          => true ); ?> 

I would attack this using the Query Monitor plugin.

As others have suggested, there are a few reasons this may not be working the way you expect. One is that your query is being altered by another plugin. Another is that the query is fine but the post_count is not what you expect. To get to the bottom of things you need to see exactly what's going on with your query.

Install and activate the Query Monitor plugin. Load the page that runs your query then open the query monitor output. Look under queries and scroll down to find yours. That will show you the query that Wordpress is running. Check it and make sure it is what you expect.

If the query has been altered then start deactivating plugins until you find the culprit.

If the query is correct then copy it, go to your database (phpmyadmin or whatever you use) and run it yourself. You will see the results directly from the database and it should be obvious why the order of records is not what you expect.

Related