Skip to content

Instantly share code, notes, and snippets.

@chikaibeneme
Created April 18, 2025 07:35
Show Gist options
  • Select an option

  • Save chikaibeneme/a68add83f82838a81d0c23691f4530a4 to your computer and use it in GitHub Desktop.

Select an option

Save chikaibeneme/a68add83f82838a81d0c23691f4530a4 to your computer and use it in GitHub Desktop.
orphaned post cleanup.php
<?php
/**
* usk8-tec-cleanup.php
*
* Standalone cleanup script for:
* A) Orphaned orders (post type: tec_tc_order) whose event (from _tec_tc_order_events_in_order)
* no longer exists – along with their associated attendees (found via ticket type IDs stored in
* _tec_tc_order_tickets_in_order).
* B) Orphaned tickets (post type: tec_tc_ticket) whose meta key (_tec_tickets_commerce_event)
* references a non‑existent event.
*
* The script:
* - Loads WordPress (assuming wp-load.php is in the current directory).
* - Enables verbose error reporting to the screen.
* - Checks that the visitor is logged in and is an administrator.
* - Part A: For each orphaned order, it retrieves the associated attendees.
* If an order has no linked attendees, an error is displayed and cleanup stops.
* - Part B: Finds orphaned tickets to delete.
* - All identified posts are then permanently deleted using wp_delete_post() so that all delete hooks fire.
* - Progress and results are output in HTML.
*/
define('LIMIT', 250);
// Enable full error reporting.
error_reporting(E_ALL);
ini_set('display_errors', 1);
ini_set('log_errors', 1);
// Load WordPress (assumes wp-load.php is in the current directory).
require_once('./wp-load.php');
// Redirect to login page if not logged in.
if ( ! is_user_logged_in() ) {
$redirect = urlencode($_SERVER['REQUEST_URI']);
wp_redirect( wp_login_url($redirect) );
exit;
}
// Ensure the user is an administrator.
if ( ! current_user_can('administrator') ) {
echo '<p>You do not have sufficient permissions to access this page.</p>';
exit;
}
global $wpdb;
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Orphaned Orders &amp; Tickets Cleanup</title>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; padding: 20px; }
h1, h2 { color: #333; }
p { margin: 0.5em 0; }
.success { color: green; }
.error { color: red; font-weight: bold; }
</style>
</head>
<body>
<h1>Orphaned Orders &amp; Tickets Cleanup Process</h1>
<?php
echo '<p>Starting cleanup process.</p>';
/* ---------------------------------------------------------------------
PART A: Cleanup Orphaned Orders & Their Associated Attendees
--------------------------------------------------------------------- */
echo '<h2>Part A: Cleaning Orphaned Orders &amp; Their Attendees</h2>';
// Query orphaned orders: orders whose meta _tec_tc_order_events_in_order references a missing event.
$orphan_order_ids = $wpdb->get_col(
"SELECT p.ID
FROM {$wpdb->posts} AS p
LEFT JOIN {$wpdb->postmeta} AS pm
ON (p.ID = pm.post_id AND pm.meta_key = '_tec_tc_order_events_in_order')
LEFT JOIN {$wpdb->posts} AS event
ON (pm.meta_value = event.ID)
WHERE p.post_type = 'tec_tc_order'
AND (pm.meta_id IS NULL OR event.ID IS NULL)
LIMIT ".LIMIT
);
if ( empty($orphan_order_ids) ) {
echo '<p>No orphaned orders found.</p>';
} else {
echo '<p>Found ' . count($orphan_order_ids) . ' orphaned orders.</p>';
if(count($orphan_order_ids)==LIMIT) echo '<p class="error">'.LIMIT.' was reached, refresh to do more.</p>';
}
// Array to hold IDs to delete for Part A.
$order_cleanup_ids = array();
// Process each orphaned order.
foreach ( $orphan_order_ids as $order_id ) {
echo '<p>Processing Order ID: ' . $order_id . '</p>';
// Add the order itself for deletion.
$order_cleanup_ids[] = $order_id;
// Retrieve the event ID from the order meta.
$event_id = get_post_meta( $order_id, '_tec_tc_order_events_in_order', true );
if ( empty($event_id) ) {
echo '<p class="error">Order ' . $order_id . ' does not have a valid event ID.</p>';
}
// For each ticket type, look up attendees using:
// - _tec_tickets_commerce_ticket equals the ticket type ID, and
// - _tec_tickets_commerce_event equals the event ID.
$attendees = get_posts( array(
'post_type' => 'tec_tc_attendee',
'post_parent' => $order_id,
'posts_per_page' => -1, // Retrieve all results.
'fields' => 'ids', // Return only the IDs (or remove this to get full objects).
) );
if ( ! empty($attendees) ) {
echo '<p>Order ' . $order_id . ' for Event '.$event_id.' found ' . count($attendees) . ' attendee(s).</p>';
}
else {
echo '<p class="error">Error: Order ' . $order_id . ' for Event '.$event_id.' has no linked attendees.</p>';
}
// Add the found attendees to the deletion list.
$order_cleanup_ids = array_merge( $order_cleanup_ids, $attendees );
}
// Remove duplicate IDs from Part A.
$order_cleanup_ids = array_unique( $order_cleanup_ids );
echo '<p>Part A: Total posts (orphaned orders &amp; their attendees) to delete: ' . count($order_cleanup_ids) . '</p>';
/* ---------------------------------------------------------------------
PART B: Cleanup Orphaned Tickets
--------------------------------------------------------------------- */
echo '<h2>Part B: Cleaning Orphaned Tickets</h2>';
// Query orphaned tickets: tickets where _tec_tickets_commerce_event points to a missing event.
$orphan_ticket_ids = $wpdb->get_col(
"SELECT t.ID
FROM {$wpdb->posts} AS t
LEFT JOIN {$wpdb->postmeta} AS pm ON (t.ID = pm.post_id AND pm.meta_key = '_tec_tickets_commerce_event')
LEFT JOIN {$wpdb->posts} AS event ON (pm.meta_value = event.ID)
WHERE t.post_type = 'tec_tc_ticket'
AND event.ID IS NULL"
);
if ( empty($orphan_ticket_ids) ) {
echo '<p>No orphaned tickets found.</p>';
} else {
echo '<p>Found ' . count($orphan_ticket_ids) . ' orphaned tickets.</p>';
}
// Array for Part B deletion.
$ticket_cleanup_ids = array();
// Process each orphaned ticket.
foreach ( $orphan_ticket_ids as $ticket_id ) {
echo '<p>Processing Ticket ID: ' . $ticket_id . '</p>';
// Add the ticket itself for deletion.
$ticket_cleanup_ids[] = $ticket_id;
}
// Remove duplicates from Part B.
$ticket_cleanup_ids = array_unique( $ticket_cleanup_ids );
echo '<p>Part B: Total posts (orphaned tickets) to delete: ' . count($ticket_cleanup_ids) . '</p>';
/* ---------------------------------------------------------------------
FINAL DELETION: Delete all identified posts
--------------------------------------------------------------------- */
echo '<h2>Final Deletion</h2>';
$all_posts_to_delete = array_merge( $order_cleanup_ids, $ticket_cleanup_ids );
$all_posts_to_delete = array_unique( $all_posts_to_delete );
echo '<p>Total posts to delete: ' . count($all_posts_to_delete) . ' (IDs: ' . implode(', ', $all_posts_to_delete) . ')</p>';
// Loop through each ID and delete the post permanently.
foreach ( $all_posts_to_delete as $post_id ) {
$result = wp_delete_post( $post_id, true );
if ( $result ) {
echo '<p class="success">Deleted post ID: ' . $post_id . '</p>';
} else {
echo '<p class="error">Failed to delete post ID: ' . $post_id . '</p>';
}
}
echo '<p>Cleanup process completed.</p>';
?>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment