Build WordPress plugins from scratch: actions and filters, shortcodes, settings pages, custom post types, meta boxes, AJAX, REST endpoints, custom tables, cron and blocks.
Lessons
Transients cache the result of slow work — an API call, a heavy query — for a set time, and WP-Cron runs your functions on a schedule, such as a daily cleanup; together they keep a plugin fast and its data tidy.
<?php
function gdc_books_get_stats() {
$stats = get_transient( 'gdc_books_stats' );
if ( false === $stats ) {
// Cache miss: do the expensive work once...
$stats = array(
'books' => (int) wp_count_posts( 'gdc_book' )->publish,
'genres' => (int) wp_count_terms( array( 'taxonomy' => 'gdc_genre' ) ),
'favourites' => gdc_books_count_favourites(),
);
// ...then keep it for 12 hours.
set_transient( 'gdc_books_stats', $stats, 12 * HOUR_IN_SECONDS );
}
return $stats;
}get_transient() returns false when the value is missing or expired, so test with false === — a cached 0 or empty array is a valid hit.MINUTE_IN_SECONDS, HOUR_IN_SECONDS, DAY_IN_SECONDS, WEEK_IN_SECONDS.Twelve hours is a maximum, not a requirement. Clear the cache the moment its data changes:
<?php
function gdc_books_clear_stats() {
delete_transient( 'gdc_books_stats' );
}
add_action( 'save_post_gdc_book', 'gdc_books_clear_stats' );
add_action( 'deleted_post', 'gdc_books_clear_stats' );<?php
// 1. Schedule on activation (only once).
function gdc_books_schedule_cleanup() {
if ( ! wp_next_scheduled( 'gdc_books_daily_cleanup' ) ) {
wp_schedule_event( time() + HOUR_IN_SECONDS, 'daily', 'gdc_books_daily_cleanup' );
}
}
register_activation_hook( __FILE__, 'gdc_books_schedule_cleanup' );
// 2. The job itself: an ordinary action.
function gdc_books_run_cleanup() {
global $wpdb;
$wpdb->query(
$wpdb->prepare(
"DELETE FROM {$wpdb->prefix}gdc_books_favourites WHERE book_id NOT IN (SELECT ID FROM {$wpdb->posts} WHERE post_type = %s)",
'gdc_book'
)
);
delete_transient( 'gdc_books_stats' );
}
add_action( 'gdc_books_daily_cleanup', 'gdc_books_run_cleanup' );
// 3. Unschedule on deactivation.
function gdc_books_unschedule_cleanup() {
wp_clear_scheduled_hook( 'gdc_books_daily_cleanup' );
}
register_deactivation_hook( __FILE__, 'gdc_books_unschedule_cleanup' );Built-in recurrences are hourly, twicedaily, daily and weekly. Add your own with the cron_schedules filter:
<?php
add_filter( 'cron_schedules', function ( $schedules ) {
$schedules['gdc_every_fifteen_minutes'] = array(
'interval' => 15 * MINUTE_IN_SECONDS,
'display' => __( 'Every 15 minutes', 'gdc-books' ),
);
return $schedules;
} );WP-Cron only runs when someone visits the site. A quiet site might run a "daily" job hours late; a busy one checks on every page load. For reliable timing, disable the visit trigger and call WP-Cron from the server's real cron:
// In wp-config.php
define( 'DISABLE_WP_CRON', true );# Server crontab: run due events every 5 minutes
*/5 * * * * wget -q -O - https://example.com/wp-cron.php?doing_wp_cron >/dev/null 2>&1Tip: Install the WP Crontrol plugin to see every scheduled event, run one immediately, and check whether yours is registered.
save_post_gdc_book.In accordance with Section 6 & Section 7 of the DPDP Act 2023, third-party analytics and marketing trackers are strictly gated behind your affirmative consent. You may withdraw consent at any time under Section 6(4).
Essential for session authentication (ci_session), Cross-Site Request Forgery security (csrf_cookie_name), and Cloudflare DDoS/bot prevention. Cannot be disabled.
Gated under DPDP Section 7. Allows Google Analytics 4 (Google Consent Mode v2) to measure aggregate, anonymized tool usage.
Enables browser local storage to preserve your theme selection (dark/light mode) and code editor layout between visits.
Used to display developer sponsorships and native tool partner notices.