WP Scheduled Events Best Practice

Hello all,

I wanted to turn to you all to get some advise before I embark on my first WP Scheduled event solution. I am looking to employ a cron job to remove expired events. This is not a robust solution for a complex event scheduler it's a simple date (maybe time) based event.

I use formidable pro to create the events and render views of all the events. When a user creates an event I have formidable also generate a post with the event information as well as a tag for the google map plugin. The purpose of the event expiration is to remove the post, so that the event will no longer appear on the map.

The best practice advice I am seeking is to chose between two approaches to the solution.

Solution one:

I can create a recurring event that on some interval (day or hour) looks at all the event posts and determines if the event has expired and then deletes the post.

Solution two:

I can create a one time event for each post upon creation that sets an expiration time based on the post custom fields date (and time, hopefully, for better acccuracy)

My concern is that creating events for eah post might be A LOT of events, and I do not know if that poses a problem for WP. I am assuming that a recurring "sweep" of all posts is a less resource intensive process. I do now know for certain, though, since a sweep across all posts each time someone visits site on an hourly basis may be more consuming than single events deleting each post on it's own.

I appreciate your feedback on this. Once I get some direction, perhaps I can present some code for your consideration.

Thank you,

Jamie

  • jamie

    Ivan,

    I am leaning towards the second option as well. It will be more specific to each event. The risk may be in setting events to cancel at one time, then having the event modified and then requiring to reset the event cancellation.

    The code I am starting with was borrowed from an expert on the subject here: http://www.sitepoint.com/mastering-wordpress-cron/

    Here is the source I will start with, and will have to modify the time based on custom fields contained in the post. This code is designed to have post expire after 30 days.

    <?php

    // delete_post_after_expiration will be called by Cron
    // We are going to be passing the Post ID so we need to specify that
    // we'll need 1 argument passed to the function

    add_action( 'delete_post_after_expiration', 'delete_post_after_expiration', 10, 1 );

    // This function will run once the 'delete_post_after_expiration' is called
    function delete_post_after_expiration( $post_id ) {

    // Takes care of deleting the specified Post
    wp_delete_post( $post_id, true );
    }

    <?php

    // schedule_post_expiration_event runs when a Post is Published

    add_action( 'publish_post', 'schedule_post_expiration_event' );

    function schedule_post_expiration_event( $post_id ) {

    // Schedule the actual event
    wp_schedule_single_event( 30 * DAY_IN_SECONDS, 'delete_post_after_expiration', array( $post_id ) );
    }

    This should be a real adventure to say the least :slight_smile:.

    Jamie

  • Tyler Postle

    Hey Jamie,

    Sounds like you have a fun project on your hands here :slight_smile:

    Looking good so far. I also want to mention that our Events+ plugin has an add-on to expire archived events, removing them from the archive. You're using your own custom solution but perhaps taking a look at that could help with decisions on your own solution.

    Hope this helps Jamie. Let us know if you have any further questions throughout your development here.

    All the best,
    Tyler

  • jamie

    Brief update.

    Not sure if you tried it, but the script to add a single event to a post broke the post creation process - post were immediately deleted, or at least I got an error saying I was trying to create/edit a post that doesn't exist.

    I have reverted to a daily solution which I am building. It should suffice for now. And if I need I can look into running a another action every hour on an intraday basis based on the time field.

    Since I have confirmed the addition of the meta data on the posts, I now embark on gathering posts that have expired, then proceeding to delete them.

    Here goes nothing :slight_smile:

    Jamie

  • jamie

    I finally sat down last night to tackle this project, and here is what I came up with. Does anyone see any potential problems? One concern is the evaluation of the DATE type. Not sure I need to convert to UNIX time.

    Is it best practice to create this as a plugin for debugging purposes and ease of use, versus just plopping into functions.php? Any feedback is appreciated before I install the code.

    function expired_post_delete() {
    
    $todays_date = the_date();
    
    $args = array(
    	'meta_query' => array(
    		array(
    			'post_type' => 'post',
    			'posts_per_page' => -1,
    			'key' => 'date',
    			'value' => '$todays_date',
    			'type' => 'DATE',
    			'compare' => '<'
    		)
    	)
    );
    
        $posts = new WP_Query( $args );
    
        // Cycle through each Post ID
    
        foreach( $posts as $post_id ) {
            wp_delete_post( $post_id);
        }
    }
    
    // Add function to register event to WordPress init
    add_action( 'init', 'register_daily_post_delete_event');
    
    // Function that will register the event
    function register_daily_post_delete_event() {
    
         // Make sure this event hasn't been scheduled
        if( !wp_next_scheduled( 'expired_post_delete' ) ) {
    
             // Schedule the event
            wp_schedule_event( time(), 'daily', 'expired_post_delete' );
        }
    }
  • aristath

    Hello there @jamie, I hope you're well today!

    Your code looks ok to me...

    Is it best practice to create this as a plugin for debugging purposes and ease of use, versus just plopping into functions.php?

    Definitely!!
    all custom code should normally be deployed as a plugin... putting it in the theme's functions.php file is easier, so it is the recommended method just because most people find it confusing having to create a plugin. :slight_smile:

    Cheers,
    Ari.

  • jamie

    Ari,

    In the above query is $post_id provided variable from WP_Query() or do I need a field in my $args array to hold that value?

    Reasoning: I see some people include 'fields' => 'ids' in their $args array, and I assumed this was a demonstration of the format for custom fields in the query - my concern is that it is required to hold the post_id value.

    Thank you for taking a look.

    Jamie

  • jamie

    I had to move some braces around, I think the WP_Query loop was erroneously including the second loop that was to go through the $posts array and delete.

    Am I missing anything? Here is what I think is the complete plugin:

    <?php
    /**
     * Plugin Name: Expired Post Delete.
     * Plugin URI: http://keefermedia.com/expired-post-delete/
     * Description: Delete expired posts based on date field custom data.
     * Version: 1.0.0
     * Author: Jamie Keefer
     * Author URI: http://keefermedia.com
     * Network: Optional. Whether the plugin can only be activated network wide. Example: true
     * License: A short license name. Example: GPL2
     */
    
     /*  Copyright 2014  Jamie Keefer  (email : jamie@keefermedia.com)
    
        This program is free software; you can redistribute it and/or modify
        it under the terms of the GNU General Public License, version 2, as
        published by the Free Software Foundation.
    
        This program is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.
    
        You should have received a copy of the GNU General Public License
        along with this program; if not, write to the Free Software
        Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    // expired_post_delete will be call when the Cron is executed
    add_action( 'expired_post_delete', 'delete_all_post_revisions' );
    
    // This function will run once the 'expired_post_delete' is called
    
    function expired_post_delete() {
    
    $todays_date = the_date();
    
    $args = array(
    	'meta_query' => array(
    		array(
    			'post_type' => 'post',
    			'posts_per_page' => -1,
    			'key' => 'date',
    			'value' => '$todays_date',
    			'type' => 'DATE',
    			'compare' => '<'
    		)
    	)
    );
         $posts = new WP_Query( $args );
    } 
    
    	// Cycle through each Post ID
    
        foreach( (array)$posts->posts as $post_id ) {
    
        wp_delete_post( $post_id);
        }
    
    // Add function to register event to WordPress init
    add_action( 'init', 'register_daily_post_delete_event');
    
    // Function which will register the event
    function register_daily_post_delete_event() {
        // Make sure this event hasn't been scheduled
        if( !wp_next_scheduled( 'expired_post_delete' ) ) {
            // Schedule the event
            wp_schedule_event( time(), 'daily', 'expired_post_delete' );
        }
    }
    
    ?>
  • aristath

    Hello again @jamie,

    Try this:

    <?php
    /**
    * Plugin Name: Expired Post Delete.
    * Plugin URI: http://keefermedia.com/expired-post-delete/
    * Description: Delete expired posts based on date field custom data.
    * Version: 1.0.0
    * Author: Jamie Keefer
    * Author URI: http://keefermedia.com
    * Network: Optional. Whether the plugin can only be activated network wide. Example: true
    * License: A short license name. Example: GPL2
    */
    
    /*  Copyright 2014  Jamie Keefer  (email : jamie@keefermedia.com)
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License, version 2, as
    published by the Free Software Foundation.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    // expired_post_delete will be call when the Cron is executed
    add_action( 'expired_post_delete', 'delete_all_post_revisions' );
    
    /**
     * This function will run once the 'expired_post_delete' is called
     */
    function expired_post_delete() {
    
    	$todays_date = the_date();
    
    	$args = array(
    		'meta_query' => array(
    			array(
    				'post_type' => 'post',
    				'posts_per_page' => -1,
    				'key' => 'date',
    				'value' => '$todays_date',
    				'type' => 'DATE',
    				'compare' => '<'
    			)
    		)
    	);
    
    	$posts = new WP_Query( $args );
    	foreach( (array)$posts->posts as $post_id ) {
    		wp_delete_post( $post_id);
    	}
    
    }
    // Add function to register event to WordPress init
    add_action( 'init', 'register_daily_post_delete_event');
    
    // Function which will register the event
    function register_daily_post_delete_event() {
    	// Make sure this event hasn't been scheduled
    	if( !wp_next_scheduled( 'expired_post_delete' ) ) {
    		// Schedule the event
    		wp_schedule_event( time(), 'daily', 'expired_post_delete' );
    	}
    }

    I think the problem was that foreach was outside the function.

    Let me know if that works for you!

    Cheers,
    Ari.

  • jamie

    Ari,

    Thank you for the catch on the brackets.

    Perhaps you can help me out on one more thing regarding foreach() syntax.

    I have foreach( (array)$posts->posts as $post_id ) and I am assuming that is correct for addressing WP post arrays.

    But how is that different than just: foreach( $posts as $post_id ). I see this used more often and not sure how they are different.

    Thank you for any help with this.

    Jamie

  • aristath

    Hello again @jamie,

    I have foreach( (array)$posts->posts as $post_id ) and I am assuming that is correct for addressing WP post arrays.

    But how is that different than just: foreach( $posts as $post_id ). I see this used more often and not sure how they are different.

    Well, I haven't tried if what you're doing is working, but the standard way to do this would look more like this:

    <?php
    /**
    * Plugin Name: Expired Post Delete.
    * Plugin URI: http://keefermedia.com/expired-post-delete/
    * Description: Delete expired posts based on date field custom data.
    * Version: 1.0.0
    * Author: Jamie Keefer
    * Author URI: http://keefermedia.com
    * Network: Optional. Whether the plugin can only be activated network wide. Example: true
    * License: A short license name. Example: GPL2
    */
    
    /*  Copyright 2014  Jamie Keefer  (email : jamie@keefermedia.com)
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License, version 2, as
    published by the Free Software Foundation.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    */
    
    // expired_post_delete will be call when the Cron is executed
    add_action( 'expired_post_delete', 'delete_all_post_revisions' );
    
    /**
    * This function will run once the 'expired_post_delete' is called
    */
    function expired_post_delete() {
    
        $todays_date = the_date();
    
        $args = array(
            'meta_query' => array(
                array(
                    'post_type' => 'post',
                    'posts_per_page' => -1,
                    'key' => 'date',
                    'value' => '$todays_date',
                    'type' => 'DATE',
                    'compare' => '<'
                )
            )
        );
    
        $query = new WP_Query( $args );
    
        if ( $query->have_posts() ) {
            while ( $query->have_posts() ) {
                $query->the_post();
                wp_delete_post( get_the_ID() );
            }
        }
        // Reset the master WordPress query
        wp_reset_postdata();
    }
    // Add function to register event to WordPress init
    add_action( 'init', 'register_daily_post_delete_event');
    
    // Function which will register the event
    function register_daily_post_delete_event() {
        // Make sure this event hasn't been scheduled
        if( !wp_next_scheduled( 'expired_post_delete' ) ) {
            // Schedule the event
            wp_schedule_event( time(), 'daily', 'expired_post_delete' );
        }
    }

    So instead of this:

    $posts = new WP_Query( $args );
    foreach( (array)$posts->posts as $post_id ) {
        wp_delete_post( $post_id);
    }

    we would use this:

    $query = new WP_Query( $args );
    
    if ( $query->have_posts() ) {
        while ( $query->have_posts() ) {
            $query->the_post();
            wp_delete_post( get_the_ID() );
        }
    }
    // Reset the master WordPress query
    wp_reset_postdata();

    I hope that helps!

    Cheers,
    Ari.

  • jamie

    Ari,

    I will give your loop a try...heaven knows I have tried everything I can think of.

    What I needed to make that style of loop work was the method: get_the_ID() so I am optimistic we may be good to go.

    It's crazy how much information there is out there that is either out of date or just plain wrong. All I can hope is that at least I am learning along the way.

    I'll get back to you with the results. I have purged the database and have begun building fresh test posts.

    Thank you again!

    Jamie

Thank NAME, for their help.

Let NAME know exactly why they deserved these points.

Gift a custom amount of points.