Skip to content

Commit

Permalink
Cache count_user_posts()
Browse files Browse the repository at this point in the history
  • Loading branch information
swissspidy committed Dec 11, 2024
1 parent 32880ec commit 511202c
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 25 deletions.
4 changes: 4 additions & 0 deletions src/wp-includes/default-filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@
add_action( $action, 'wp_maybe_update_user_counts', 10, 0 );
}

// User post counts.
add_action( 'post_updated', '_clear_user_posts_count_cache_on_author_change', 10, 3 );
add_action( 'save_post', '_clear_user_posts_count_cache_on_update', 10, 2 );

// Post meta.
add_action( 'added_post_meta', 'wp_cache_set_posts_last_changed' );
add_action( 'updated_post_meta', 'wp_cache_set_posts_last_changed' );
Expand Down
113 changes: 101 additions & 12 deletions src/wp-includes/user.php
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,97 @@ function wp_validate_logged_in_cookie( $user_id ) {
return wp_validate_auth_cookie( $_COOKIE[ LOGGED_IN_COOKIE ], 'logged_in' );
}

/**
* Counts the number of posts for a particular post type.
*
* @since 6.8.0
*
* @param int $user_id User ID.
* @param string $post_type Optional. Post type to count the number of posts for. Default 'post'.
* @param bool $public_only Optional. Whether to only return counts for public posts. Default false.
* @return int Number of posts the user has written in this post type.
*/
function count_user_posts_for_post_type( $user_id, $post_type = 'post', $public_only = false ) {
global $wpdb;

$where = get_posts_by_author_sql( $post_type, true, $user_id, $public_only );
$where_hash = wp_hash( $where );
$cache_key = "count_user_{$post_type}_{$user_id}_{$where_hash}";
$cache_group = $public_only ? 'user_posts_count_public' : 'user_posts_count';

// Try to get count from cache.
$count = wp_cache_get( $cache_key, $cache_group );

// If cache is empty, query the database.
if ( false === $count ) {
$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts $where" );

wp_cache_add( $cache_key, $count, $cache_group );
}

return absint( $count );
}

/**
* Clears the cached posts count for user for given post type.
*
* @since 6.8.0
*
* @param int $user_id User ID.
* @param string $post_type Post type.
*/
function clear_user_posts_count_cache( $user_id, $post_type ) {
$cache_key = "count_user_{$post_type}_{$user_id}";
$cache_groups = array(
'user_posts_count_public',
'user_posts_count',
);

foreach ( $cache_groups as $cache_group ) {
wp_cache_delete( $cache_key, $cache_group );
}
}

/**
* Clears the cached posts count for both users if a post's author changes.
*
* @since 6.8.0
* @access private
*
* @param int $post_id Post ID.
* @param WP_Post $post_after Post object following the update.
* @param WP_Post $post_before Post object before the update.
*/
function _clear_user_posts_count_cache_on_author_change( $post_id, $post_after, $post_before ) {
if ( $post_after->post_author !== $post_before->post_author ) {
$post_type = get_post_type( $post_id );

clear_user_posts_count_cache( $post_after->post_author, $post_type );
clear_user_posts_count_cache( $post_before->post_author, $post_type );
}
}

/**
* When the post is created, clear the cache.
*
* @since 6.8.0
* @access private
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
*/
function _clear_user_posts_count_cache_on_update( $post_id, $post ) {
// Don't do anything if revision is being saved.
if ( wp_is_post_revision( $post_id ) ) {
return;
}

$post_type = $post->post_type;
$author_id = $post->post_author;

clear_user_posts_count_cache( $author_id, $post_type );
}

/**
* Gets the number of posts a user has written.
*
Expand All @@ -579,14 +670,18 @@ function wp_validate_logged_in_cookie( $user_id ) {
* @param int $userid User ID.
* @param array|string $post_type Optional. Single post type or array of post types to count the number of posts for. Default 'post'.
* @param bool $public_only Optional. Whether to only return counts for public posts. Default false.
* @return string Number of posts the user has written in this post type.
* @return int Number of posts the user has written in this post type.
*/
function count_user_posts( $userid, $post_type = 'post', $public_only = false ) {
global $wpdb;
if ( is_string( $post_type ) ) {
$post_type = array( $post_type );
}

$where = get_posts_by_author_sql( $post_type, true, $userid, $public_only );
$count = 0;

$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts $where" );
foreach ( $post_type as $type ) {
$count += count_user_posts_for_post_type( $userid, $type, $public_only );
}

/**
* Filters the number of posts a user has written.
Expand Down Expand Up @@ -616,19 +711,13 @@ function count_user_posts( $userid, $post_type = 'post', $public_only = false )
* @return string[] Amount of posts each user has written, as strings, keyed by user ID.
*/
function count_many_users_posts( $users, $post_type = 'post', $public_only = false ) {
global $wpdb;

$count = array();
if ( empty( $users ) || ! is_array( $users ) ) {
return $count;
}

$userlist = implode( ',', array_map( 'absint', $users ) );
$where = get_posts_by_author_sql( $post_type, true, null, $public_only );

$result = $wpdb->get_results( "SELECT post_author, COUNT(*) FROM $wpdb->posts $where AND post_author IN ($userlist) GROUP BY post_author", ARRAY_N );
foreach ( $result as $row ) {
$count[ $row[0] ] = $row[1];
foreach ( $users as $user_id ) {
$count[ $user_id ] = count_user_posts( $user_id, $post_type, $public_only );
}

foreach ( $users as $id ) {
Expand Down
16 changes: 8 additions & 8 deletions tests/phpunit/tests/user.php
Original file line number Diff line number Diff line change
Expand Up @@ -559,21 +559,21 @@ public function test_count_many_users_posts() {

wp_set_current_user( self::$author_id );
$counts = count_many_users_posts( array( self::$author_id, $user_id_b ), 'post', false );
$this->assertSame( '1', $counts[ self::$author_id ] );
$this->assertSame( '1', $counts[ $user_id_b ] );
$this->assertSame( 1, $counts[ self::$author_id ] );
$this->assertSame( 1, $counts[ $user_id_b ] );

$counts = count_many_users_posts( array( self::$author_id, $user_id_b ), 'post', true );
$this->assertSame( '1', $counts[ self::$author_id ] );
$this->assertSame( '1', $counts[ $user_id_b ] );
$this->assertSame( 1, $counts[ self::$author_id ] );
$this->assertSame( 1, $counts[ $user_id_b ] );

wp_set_current_user( $user_id_b );
$counts = count_many_users_posts( array( self::$author_id, $user_id_b ), 'post', false );
$this->assertSame( '1', $counts[ self::$author_id ] );
$this->assertSame( '2', $counts[ $user_id_b ] );
$this->assertSame( 1, $counts[ self::$author_id ] );
$this->assertSame( 2, $counts[ $user_id_b ] );

$counts = count_many_users_posts( array( self::$author_id, $user_id_b ), 'post', true );
$this->assertSame( '1', $counts[ self::$author_id ] );
$this->assertSame( '1', $counts[ $user_id_b ] );
$this->assertSame( 1, $counts[ self::$author_id ] );
$this->assertSame( 1, $counts[ $user_id_b ] );
}

/**
Expand Down
10 changes: 5 additions & 5 deletions tests/phpunit/tests/user/countUserPosts.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,34 +59,34 @@ public function set_up() {
}

public function test_count_user_posts_post_type_should_default_to_post() {
$this->assertSame( '4', count_user_posts( self::$user_id ) );
$this->assertSame( 4, count_user_posts( self::$user_id ) );
}

/**
* @ticket 21364
*/
public function test_count_user_posts_post_type_post() {
$this->assertSame( '4', count_user_posts( self::$user_id, 'post' ) );
$this->assertSame( 4, count_user_posts( self::$user_id, 'post' ) );
}

/**
* @ticket 21364
*/
public function test_count_user_posts_post_type_cpt() {
$this->assertSame( '3', count_user_posts( self::$user_id, 'wptests_pt' ) );
$this->assertSame( 3, count_user_posts( self::$user_id, 'wptests_pt' ) );
}

/**
* @ticket 32243
*/
public function test_count_user_posts_with_multiple_post_types() {
$this->assertSame( '7', count_user_posts( self::$user_id, array( 'wptests_pt', 'post' ) ) );
$this->assertSame( 7, count_user_posts( self::$user_id, array( 'wptests_pt', 'post' ) ) );
}

/**
* @ticket 32243
*/
public function test_count_user_posts_should_ignore_non_existent_post_types() {
$this->assertSame( '4', count_user_posts( self::$user_id, array( 'foo', 'post' ) ) );
$this->assertSame( 4, count_user_posts( self::$user_id, array( 'foo', 'post' ) ) );
}
}

0 comments on commit 511202c

Please sign in to comment.