3

how can I use Cron to delete posts of a certain post type once they reach a certain limit, like keep maximum 50 posts?

The reason is that these posts are being imported automatically periodically, so I want to prevent the DB getting too large, apart from the fact that the older posts won't be needed.

3
  • is it 50 posts per author? or 50 posts from all author?
    – ifdion
    Commented Aug 28, 2012 at 9:06
  • 50 posts from all authors for a particular custom post type.
    – urok93
    Commented Aug 28, 2012 at 9:23
  • For anyone interested, I've found a plugin which does something similar, wordpress.org/extend/plugins/post-expirator
    – urok93
    Commented Aug 28, 2012 at 22:27

2 Answers 2

13
+50

Although I do not understand the motivation for truncating posts, I think this exercise is valuable for you to understand how to use cron + WordPress.

Create a function to truncate posts

This can be used for both methods below WP-cron or UNIX cron.

function foobar_truncate_posts(){
    global $wpdb;

    # Set your threshold of max posts and post_type name
    $threshold = 50;
    $post_type = 'foobar';

    # Query post type
    $query = "
        SELECT ID FROM $wpdb->posts 
        WHERE post_type = '$post_type' 
        AND post_status = 'publish' 
        ORDER BY post_modified DESC
    ";
    $results = $wpdb->get_results($query);

    # Check if there are any results
    if(count($results)){
        foreach($result as $post){
            $i++;

            # Skip any posts within our threshold
            if($i <= $threshold)
                continue;

            # Let the WordPress API do the heavy lifting for cleaning up entire post trails
            $purge = wp_delete_post($post->ID);
        }
    }
}

Here are the two basic approaches to scheduling events in WordPress.

Approach #1: Using WP-Cron

Since this is the WP way of doing this, we'll look at this approach first. Please note, WP Cron is not true cron, and it's oft called psuedo-cron. It is not consistent if you have low traffic on a site as it is based on requests to the server. If no requests come in, then your scheduled event runs late.

Schedule your event

if(!wp_next_scheduled( 'foobar_truncate_posts_schedule')){
    wp_schedule_event(time(), 'daily', 'foobar_truncate_posts_schedule');
}

Hook into your schedule action

add_action('foobar_truncate_posts_schedule', 'foobar_truncate_posts');

If you find that WP-Cron is missing your schedule, publishing scheduled posts, etc..., you can automate it further with a UNIX cron. Here's a great article to show you how to ping wp-cron.php at specified intervals. Here's what they recommend to keep wp-cron on-time using a UNIX cron.

wget http://www.server.com/wp-cron.php > /dev/null 2>&1

Approach #2: Using a UNIX cron

You can use true UNIX crons with native admin-ajax.php functionality.

Verify cURL on your server

This approach uses cURL which should be installed on your server. If not and you're using Apache, sudo apt-get install php5-curl and then sudo /etc/init.d/apache2 restart.

Create an AJAX hook

Make sure to set it to nopriv as your server wont be authenticating with WP.

add_action('wp_ajax_nopriv_truncate_posts', 'foobar_truncate_posts_cron');
function foobar_truncate_posts_cron(){

    # We use the user-agent as a shared key
    $shared_user_agent = 'FooBar TruncatePostsCron/1.0';

    # Block unwanted IP addresses
    $whitelisted_ips = array( //IPs allowed to run this operation
        '192.168.1.1',
        '127.0.0.1'
    );

    # Retrive Request Information
    $request_user_agent = $_SERVER['HTTP_USER_AGENT'];
    $request_ip = $_SERVER['REMOTE_ADDR'];


    # Authenticate
    if($request_user_agent === $shared_user_agent && in_array($request_ip, $whitelisted_ips))
        echo foobar_truncate_posts(); // Reusable function
    else
        echo 'Authentication failed for post trucation cron.';

    exit;
}

Add your Crontab

This config will run one time per day consistently. -A sets the shared user agent secret -o specifies an output file, action=truncate_posts is relative to your ajax hook action. Verify /user/bin/curl is a proper path for executing a cURL command. You might be able to just use curl instead.

0 0 * * * /usr/bin/curl -A 'FooBar TruncatePostsCron/1.0' -o ~/truncate_posts.log http://yourdomain.com/wp-admin/admin-ajax.php?action=truncate_posts

And finally, always make sure you set register_globals=off in your php.ini to prevent spoofing of any sort.

And, Finally...

These are the two main approaches to WordPress + cron (whether true or not). There are many ways to skin a cat with your specific use case in foobar_truncate_posts(). I'm sure you can tweak it from here. Hope this helps you out!

13
  • 3
    It's worth noting that WordPress has a built in cron system. IMO, I think it's better to recommend using it over a Unix cron job/"AJAX" handler because it's more accessible to beginners, and also keeps everything grouped together at the application level, instead of spreading it across different layers of the stack.
    – Ian Dunn
    Commented Aug 28, 2012 at 5:39
  • 2
    @IanDunn Good points. You should make your case in an answer. This is pretty mucha bullet-proof method for consistency. WP-Cron can easily be junked with caching mechanisms. Your approach would work just fine for a basic WordPress installation. In one enterprise install I work with, we actually have a UNIX crontab fire off a request to origin every five minutes to keep wp-cron intact since we're behind a full CDN DNS. On another note, running an expensive operation like the above on a user visit is not good UX, thus I prefer to run these on low traffic times during the night. Commented Aug 28, 2012 at 5:47
  • @BrianFegter Thanks that's an excellent explanation.
    – urok93
    Commented Aug 28, 2012 at 9:18
  • 1
    That's an excellent explanation, thanks. Why do you use the $wpdb instead of using WP_Query?
    – urok93
    Commented Aug 28, 2012 at 21:58
  • 1
    This is a seriously excellent answer. Another reason one might prefer to use a UNIX cronjob to kick off an action via admin-ajax.php would be to avoid any potential race conditions in massively distributed environments, where multiple visits to the WordPress install at the same moment to different machines could cause the WP cron task to run more than once. For this reason, we prefer the UNIX-based approach, since it guarantees that only one client will ever cause the job to run at a time.
    – Bendoh
    Commented Aug 29, 2012 at 22:53
0

The most solid method would be to use WP-Cron itself. But if you have a very light reqirement, you can leave it as such and no issues like multiple visits tiggering the wp-cron multiple times and events overlapping will never occur.

Should refer to Is it safe to run wp-cron.php twice if the first instance takes too long?

It is clearly stated there ( actually the answer which deserves more votes ) has the statement in the thrid paragraph "because wp-cron reschedules and unschedules jobs as it goes. Right before it runs a job, it unschedules it. This prevents the job from running twice", yes there are apprehensions that there may be a race condition that might occur where a heavy bogged blog might kick the same event twice.

Now the wordpress team has a solution for the same, and that is to disable the cron in the wp-config.php. This does not disable cron, but actually disables the firing of cron when a visit occurs. After this a web cron can be setup using wget or curl but if we are not confident that services will finish inside 30 seconds, which happens to be the default timeout for a php script, then the cron should be set up on the server itself using the php cli. A more indepth document on how to do this is available at https://rtcamp.com/tutorials/wordpress/wp-cron-crontab/

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.