There are times when we perform import tests and among the various actions we perform we usually end up with a WordPress full of entries that we must empty by selecting them in batch and it is a tedious and slow process.
We can easily remove all entries from the table of posts with a simple SQL query, but we will still be left with a lot of data in the table postmeta. Yes, it can be eliminated without complications by adding a JOIN between tables, but what if it is multi-language and we have relationships in other tables? What about the YOAST or Rank Math data? The additional fields? ….
In all these cases if the plugin is well developed (which I already tell you is sadly not in most cases), it will “hook into” the appropriate hooks of the post deletion events to also delete their relative data.
Because of this, the best option to delete a WordPress post is to use its native function, which executes its hooks and in which the appropriate actions will be executed.
The native function for deleting posts is wp_delete_post() to which we pass the ID of the post and if we pass true as a second parameter, it deletes it definitively without taking it to the trash. Be careful with this last one, because if we delete Custom Post Types, even if we pass false as a second parameter, it will delete it definitively, so if we want to send these posts to the trash, it will be better to use wp_trash_post().
As we want to removeposts from the bog, we will use the get_posts() function.
In the array of arguments we pass, we use numberposts
with -1
to retrieve all the entries. If there are many entries (10,000 for example), we can run it with 1,000 entries and delete them “in batches”, since depending on the installation, many actions may be executed in the deletion hooks.
The script is as follows:
<?php
die( 'Carlos says that silence is golden' );
require_once( '../wp-load.php' );
$website_posts = get_posts( array( 'numberposts' => -1 ) );
echo '<ol>';
foreach ( $website_posts as $post ) {
// Delete all posts.
wp_delete_post( $post->ID, true ); // Set to False if you want to send them to Trash.
echo '<li>Post deleted (type: "' . $post->post_type . '", status: ' . $post->post_status . '; ID: ' . $post->ID . ') with title «' . $post->post_title . '»</li>';
}
echo '</ol>';
Of course you have to know what the first line does for the script to work, if you don’t, then refrain from copying and executing it, as it can be very dangerous for your installation.
I upload this script to a folder in the root of the installation, hence the require_once
goes up one level to load the WordPress core base.
In line 6 as I have already mentioned, we can load only a certain number of posts to delete or for example select pages by adding to array 'post_type' => 'page'
or WooCommerce products with 'post_type' => 'product'
, our CPT and other customizations.
Then I create an ordered list so that the HTML itself will number each deleted entry along with a series of data that I display, such as its ID, title, etc.
In the line 11 where we execute the wp_delete_post() we can make some check like that the $post->post_status
is wc-completed
if we had chosen 'post_type' => 'shop_order'
in order to delete all the completed orders (or with another condition only those of more than x years, etc.).
As we can see it is a very simple script, that we can customize as much as we want, but BEWARE, it is also VERY DANGEROUS. Always make backup copies and as soon as you finish using it, REMOVE IT or at least leave the die()
line present, lest someone else runs it for you when you are no longer testing. 🙁
Update 3/9/2021: Luis Ruiz has just published an excellent article in which he explains how to delete only some urls instead of all of them, for which a CSV file is created with the URLs to delete and the script takes care of the rest. Thank you, Luis.