Reputation: 197
I have a series of quotes saved in my database, and I want a singe, random quote to be pulled from the database and displayed on my site every 48 hours.
I've found code for scheduling this to happen using a custom cron scheduled event for every 48 hours. However, I'm assuming I'm implementing it incorrectly because although a single random quote is showing up on the site, it is switching to a new random quote every time the page is refreshed, as opposed to once every 48 hours.
function quote_custom_cron_schedule( $schedules ) {
$schedules['every_two_days'] = array(
'interval' => 172800, // Every 48 hours
'display' => __( 'Every 48 hours' ),
);
return $schedules;
}
add_filter( 'cron_schedules', 'quote_custom_cron_schedule' );
//Schedule an action if it's not already scheduled
if ( ! wp_next_scheduled( 'quote_cron_hook' ) ) {
wp_schedule_event( time(), 'every_two_days', 'quotes_cron_hook' );
}
//Hook into that action that'll fire every 48 hours
add_action( 'quotes_cron_hook', 'quotes_cron_function' );
//Function that executes via cron
function quotes_cron_function() {
$args = array(
'post_type' => 'quote',
'orderby' => 'rand',
'posts_per_page'=> 1,
);
$quotes = new WP_Query( $args );
$quotes_output = '';
if( $quotes->have_posts() ):
while ( $quotes->have_posts() ) : $quotes->the_post();
$quotes_title = get_the_title();
$quotes_content = get_the_content();
$quotes_output .= '<div id="quote"><p>"' . $quotes_content . '"</p><p class="quote-name">' . $quotes_title . '</p></div>';
endwhile;
else:
$quotes_output = '';
endif;
wp_reset_postdata();
echo $quotes_output;
}
I then have the following in my header.php file:
<?php do_action( 'quotes_cron_hook' ); ?>
This is executing the function, but it executes every time the page is refreshed. I need the function to wait 48 hours before changing the quote again. I feel like I'm probably missing something simple here.
Upvotes: 0
Views: 3214
Reputation: 664
I think you're thinking about this the wrong way. You have do_action( 'quotes_cron_hook' )
in your header, which will fire the quotes_cron_hook
action every time the page loads, which then allows quotes_cron_function()
to hook into that action every time it fires. This seems consistent with the behavior you reported.
The thing is, setting a WP cron job doesn't force a a hook to only run at that particular interval. In your case, the quotes_cron_hook
action fires every time the page loads, but it also fires once every 48 hours.
It seems to me like your desired functionality is to pull a "temporary" quote, but then change it every 48 hours. To do that, you would want something like:
if ( ! wp_next_scheduled( 'change_quote_hook' ) ) {
wp_schedule_event( time(), 'every_two_days', 'change_quote_hook' );
}
add_action( 'change_quote_hook', 'change_quote_function' );
function change_quote_function() {
$args = array(
'post_type' => 'quote',
'orderby' => 'rand',
'posts_per_page'=> 1,
);
$quotes = new WP_Query( $args );
$quotes_output = '';
if( $quotes->have_posts() ):
while ( $quotes->have_posts() ) :
$quotes->the_post();
// Set the temporary post using a WordPress option
update_option( 'quote_post', get_the_ID() );
endwhile;
endif;
wp_reset_postdata();
}
The cron job will then fire this function once every 48 hours (more or less as people have pointed out) and set the quote where it can then be displayed. Then, your header can do the action to display the quote every time the page loads.
add_action( 'display_quote', 'display_quote_function' );
function display_quote_function() {
// Get the quote post ID
// The default, 1, will be returned if the option is not set
$quote_id = get_option( 'quote_post', 1 );
$quote = get_post( $quote_id );
$quotes_title = quote->post_title;
$quotes_content = quote->post_content;
$quotes_output = '<div id="quote"><p>"' . $quotes_content . '"</p><p class="quote-name">' . $quotes_title . '</p></div>';
echo $quotes_output;
}
do_action('display_quote');
Upvotes: 0
Reputation: 21661
Your issue is likly this:
if ( ! wp_next_scheduled( 'quote_cron_hook' ) ) {
wp_schedule_event( time(), 'every_two_days', 'quotes_cron_hook' );
}
Where you have quote_cron_hook
insted of quotes_cron_hook
one has a s
one doesn't. This mismatch will cause the condition to never fail, because the event is named wrong, it is never scheduled, and this condition passes on every single request. Which in turns runs the wp_schedule_event
function with a start time of time
(see below).
If you look at the documentation for wp_schedule_event
link below. The first argument where you have time()
is this
$timestamp (integer) (required) The first time that you want the event to occur. This must be in a UNIX timestamp format. WP cron uses UTC/GMT time, not local time. Use time(), which is always GMT in WordPress.
Note this The first time that you want the event to occur, with time()
that is right now, and because it's named wrong it executes this every page load and then runs right now.
You can of course set time()
to something else, but it will just mask the problem.
Other stuff
This may also be wrong.
wp_schedule_event( time(), 'every_two_days', 'quotes_cron_hook' );
more specifically, looking at the documentation
https://codex.wordpress.org/Function_Reference/wp_schedule_event
$recurrence (string) (required) How often the event should reoccur. Valid values:
- hourly
- twicedaily
- daily
Default: None
I don't see every_two_days
in there, unless it's undocumented. To be honest I haven't used the WP scheduler much. The Default: None
is probably what you actual have here, as WP doesn't recognize that value so it falls back to none. But what exactly that does I am not sure.
Even more Other stuff
As I said in the comments (and that may be ok for your use, but keep it in mind)
The wp scheduler can only run when an active request takes place, so the wp scheduler can only grantee that a job will not run before a given time. There is no grantee that it will run at a specific time after that, it runs on the next request after the allotted time
Add this to wp-config.php
define('DISABLE_WP_CRON', true);
Disable the built in cron/scheduler. Then go into C-Panel or edit crontab (crontab -e) and add this cron job to run every minute (or some useful interval)
php -q pathtowp/wp-cron.php
What this does, it take the control of the scheduar timing away from wordpress and give it to the "actual" CRON application (on linux). That way it's not relying on page loads.
But, as I said and because this is a post someone should view, the normal WP behavior may be perfectly fine for you as it is. Generally if I do anything with the scheduler I just rely on real CRON, which is why I mentioned it.
Hope it helps!
Upvotes: 1