On a project I require to keep only 10 latest posts on website and auto delete all other posts. So I came up with this function which triggers on post publish/update and I used offset
parameter to exclude latest 10 items.
function wpse_auto_delete_posts( $post_ID ) {
global $post;
$delete_posts = get_posts( array( 'post_type' => 'post', 'offset' => 10, 'posts_per_page' => -1 ) );
foreach ( $delete_posts as $delete_post ) : setup_postdata( $delete_post );
wp_delete_post( $delete_post->ID, true );
endforeach;
wp_reset_postdata();
}
add_action( 'publish_post', 'wpse_auto_delete_posts' );
But it appears, offset
didn’t work here with 'posts_per_page' => -1
and it keeps deleting all posts. I thought of using Date query but it will also not work too because new posts are created very frequently.
Is there any way to select all posts except latest 10 or 5. So WordPress can process them and keep latest X posts.
2 s
The offset
parameter is ignored with posts_per_page
set to -1
in WP_Query
. If you look at the source code in the WP_Query
class, posts_per_page=-1
sets nopaging
to true.
if ( !isset($q['nopaging']) ) {
if ( $q['posts_per_page'] == -1 ) {
$q['nopaging'] = true;
} else {
$q['nopaging'] = false;
}
}
This in turn will not append the LIMIT
to the SQL query (empty($q['nopaging'] === false
which fail the conditional statement) meaning that the whole pagination/offset is ignored and all posts are returned regardless
if ( empty($q['nopaging']) && !$this->is_singular ) {
$page = absint($q['paged']);
if ( !$page )
$page = 1;
if ( empty($q['offset']) ) {
$pgstrt = absint( ( $page - 1 ) * $q['posts_per_page'] ) . ', ';
} else { // we're ignoring $page and using 'offset'
$q['offset'] = absint($q['offset']);
$pgstrt = $q['offset'] . ', ';
}
$limits="LIMIT " . $pgstrt . $q['posts_per_page'];
}
I think the best workaround here is to make use of normal PHP (array_slice()
)after getting all the posts. You want to get only post ID’s here as wp_delete_post
is quite expensive to run and only really need the post ID, so we don’t need any other post info.
In short, your query will look like this: (NOTE: This is all untested and needs PHP 5.4+)
add_action( 'publish_post', function ( $post_ID )
{
$args = [
'nopaging' => true,
'fields' => 'ids'
];
$posts_array = get_posts( $args );
// Make sure that we have post ID's returned
if ( $posts_array ) {
// Get all post ids after the 10th post using array_slice
$delete_posts = array_slice( $posts_array, 10 );
// Make sure that we actually have post ID's in the $delete_posts array
if ( $delete_posts ) {
foreach ( $delete_posts as $delete_post )
wp_delete_post( $delete_post, true );
}
}
});
EDIT
Just before I forget, you can also define posts_per_page
as an unlikely integer value instead of -1
. This will also work with your code with the offset
parameter set