I’m trying to build a custom frontend dashboard (for shop managers) where they can view and filter WooCommerce orders.
Here’s what I need:
-
Display orders in a table (order number, date, status, total, customer, actions).
-
Add filters by date range and order status.
-
Add pagination (so not all orders load at once).
-
Make sure the query is optimized and doesn’t slow down the site when there are thousands of orders.
I’m currently using WC_Order_Query with paginate => true, which works, but I’m not sure if this is the best way to handle performance and scalability.
My questions are:
-
What is the most efficient way to query and display WooCommerce orders in the frontend with filters and pagination?
-
Should I use WC_Order_Query, WP_Query, or maybe a custom SQL query for better performance?
-
Any best practices to avoid slowing down the dashboard when there are many orders?
<?php
defined('ABSPATH') || exit;
if (! is_user_logged_in() || ! current_user_can('manage_woocommerce')) {
echo '<p>Accesso negato.</p>';
return;
}
$start_date = isset($_GET['start_date']) ? esc_attr($_GET['start_date']) : '';
$end_date = isset($_GET['end_date']) ? esc_attr($_GET['end_date']) : '';
$status = isset($_GET['order_status']) ? sanitize_text_field($_GET['order_status']) : '';
$per_page = 10;
$current_page = max(1, get_query_var('paged', isset($_GET['paged']) ? intval($_GET['paged']) : 1));
$query_args = [
'paginate' => true,
'limit' => $per_page,
'paged' => $current_page,
'orderby' => 'date',
'order' => 'DESC',
'status' => array_keys(wc_get_order_statuses()),
];
// Date filter
if ($start_date && ! $end_date) {
$query_args['date_created'] = '>=' . $start_date;
} elseif (! $start_date && $end_date) {
$query_args['date_created'] = '<=' . $end_date;
} elseif ($start_date && $end_date) {
$query_args['date_created'] = $start_date . '...' . $end_date;
}
// Status filter
if (! empty($status)) {
$query_args['status'] = $status;
}
// Run query
$orders_data = (new WC_Order_Query($query_args))->get_orders();
$orders = $orders_data->orders;
$max_pages = $orders_data->max_num_pages;
$has_orders = ! empty($orders);
?>
<!-- FILTER FORM -->
<form action="#" method="get" class="woocommerce-order-filter-form form-data-filter">
<input type="hidden" name="tab" value="orders" />
From
<input class="date-input" type="date" name="start_date" value="<?php echo esc_attr($start_date); ?>">
To
<input class="date-input" type="date" name="end_date" value="<?php echo esc_attr($end_date); ?>">
<select name="order_status" class="status-select">
<option value=""><?php esc_html_e('All statuses', 'woocommerce'); ?></option>
<?php foreach (wc_get_order_statuses() as $slug => $label): ?>
<?php $pure_slug = str_replace('wc-', '', $slug); ?>
<option value="<?php echo esc_attr($pure_slug); ?>" <?php selected($status, $pure_slug); ?>>
<?php echo esc_html($label); ?>
</option>
<?php endforeach; ?>
</select>
<input class="date-submit" type="submit" value="<?php esc_attr_e('Filter', 'woocommerce'); ?>">
<?php if ($start_date || $end_date || $status): ?>
<a class="button reset" href="?tab=orders"><?php esc_html_e('Reset', 'woocommerce'); ?></a>
<?php endif; ?>
</form>
<!-- ORDERS TABLE -->
<?php if ($has_orders): ?>
<table class="gestore-tabella-ordini cpa-dasboard-table">
<thead>
<tr>
<th>Order</th>
<th>Date</th>
<th>Status</th>
<th>Total</th>
<th>Customer</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($orders as $order): ?>
<?php
if (! is_a($order, 'WC_Order')) {
continue;
}
$status_code = $order->get_status();
$status_map = [
'pending' => ['label' => 'Pending', 'icon' => 'fa-clock'],
'processing' => ['label' => 'Processing', 'icon' => 'fa-gear'],
'on-hold' => ['label' => 'On hold', 'icon' => 'fa-spinner'],
'completed' => ['label' => 'Completed', 'icon' => 'fa-circle-check'],
'cancelled' => ['label' => 'Cancelled', 'icon' => 'fa-circle-xmark'],
'refunded' => ['label' => 'Refunded', 'icon' => 'fa-arrow-rotate-left'],
'failed' => ['label' => 'Failed', 'icon' => 'fa-triangle-exclamation'],
'checkout-draft' => ['label' => 'Draft', 'icon' => 'fa-spinner'],
];
$status_data = $status_map[ $status_code ] ?? [
'label' => ucfirst($status_code),
'icon' => 'fa-question-circle',
];
?>
<tr class="woocommerce-orders-table__row status-<?php echo esc_attr($status_code); ?>">
<th scope="row">
<span class="order-number">#<?php echo esc_html($order->get_order_number()); ?></span>
</th>
<td>
<time datetime="<?php echo esc_attr($order->get_date_created()->date('c')); ?>">
<?php echo esc_html(wc_format_datetime($order->get_date_created())); ?>
</time>
</td>
<td>
<span class="status-text status-<?php echo esc_attr($status_code); ?>">
<i class="fa-solid <?php echo esc_attr($status_data['icon']); ?>"></i>
<?php echo esc_html($status_data['label']); ?>
</span>
</td>
<td>
<?php echo $order->get_formatted_order_total(); ?>
</td>
<td>
<?php echo esc_html($order->get_billing_first_name() . ' ' . $order->get_billing_last_name()); ?>
</td>
<td>
<a href="/dashboard/?dettaglio_ordine=<?php echo esc_attr($order->get_id()); ?>" class="cpa-btn-secondary">
<i class="fa-solid fa-magnifying-glass"></i>
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<!-- PAGINATION -->
<?php if ($max_pages > 1): ?>
<div class="woocommerce-pagination woocommerce-pagination--numeric woocommerce-Pagination">
<?php
$base_url = remove_query_arg('paged');
?>
<?php if ($current_page > 1): ?>
<a class="woocommerce-button woocommerce-button--previous button"
href="<?php echo esc_url(add_query_arg('paged', $current_page - 1, $base_url)); ?>">
← Previous
</a>
<?php endif; ?>
<?php for ($i = 1; $i <= $max_pages; $i++): ?>
<?php if ($i === $current_page): ?>
<span class="page-number current"><?php echo $i; ?></span>
<?php else: ?>
<a class="page-number" href="<?php echo esc_url(add_query_arg('paged', $i, $base_url)); ?>">
<?php echo $i; ?>
</a>
<?php endif; ?>
<?php endfor; ?>
<?php if ($current_page < $max_pages): ?>
<a class="woocommerce-button woocommerce-button--next button"
href="<?php echo esc_url(add_query_arg('paged', $current_page + 1, $base_url)); ?>">
Next →
</a>
<?php endif; ?>
</div>
<?php endif; ?>
<?php else: ?>
<?php wc_print_notice('No orders found.', 'notice'); ?>
<?php endif; ?>