Bài viết khác

Thêm theo dõi đơn hàng bằng số điện thoại cực đẹp trong Woocommerce

Một trong những tính năng được nhiều chủ shop tìm kiếm là thêm theo dõi đơn hàng bằng số điện thoại trong WooCommerce – tiện lợi, nhanh chóng và thân thiện với người dùng. Bài viết này sẽ hướng dẫn bạn cách thêm tính năng tra cứu đơn hàng bằng số điện thoại trong WooCommerce.

Vì Sao Nên Tạo Trang Theo Dõi Đơn Hàng Bằng Số Điện Thoại ?

  • Dễ dàng tra cứu: Khách hàng không cần nhớ mã đơn hàng rườm rà, chỉ cần số điện thoại.
  • Nâng cao trải nghiệm người dùng: Giao diện đẹp, hiện đại giúp khách hàng tin tưởng hơn.
  • Tối ưu chuyển đổi: Hỗ trợ chăm sóc sau bán hàng tốt hơn, tăng tỷ lệ quay lại.
Demo Tao trang theo doi don don cuc dep trong Woocommerce

Nên đọc kỹ code, có nhiều chỗ mình comment giải thích hoặc cần sửa cho phù hợp, nếu có sai sót hoặc thắc mắc hãy liên hệ với mình nhé

Nếu có nhu cầu mua Hosting và VPS các bạn ủng hộ mình nhé

Cách Tạo Trang Theo Dõi Đơn Hàng WooCommerce Bằng Số Điện Thoại

Bạn có thể đặt code vào file function.php trong child theme hoặc sử dụng plugin WPCode – Insert Headers and Footers + Custom Code Snippets – WordPress Code Manager để dễ dàng kiểm soát code hơn.

Bước 1: Thêm trạng thái mới cho đơn hàng

Mình thêm trạng thái “Đang giao hàng” vào Woocommerce để tối ưu về mặt trải nghiệm người dùng. Bạn nào muốn có thêm trạng thái cho hợp luồng bán hàng của mình thì thêm, không thì có thể bỏ qua nhé.

PHP
<?php
// Đăng ký trạng thái mới
add_filter( 'woocommerce_register_shop_order_post_statuses', 'register_custom_status_order' );
function register_custom_status_order( $order_statuses ) {
    $order_statuses['wc-danggiaohang'] = array(
        'label'                     => _x( 'Đang giao hàng', 'Order status', 'woocommerce' ),
        'public'                    => true,
        'exclude_from_search'       => false,
        'show_in_admin_all_list'    => true,
        'show_in_admin_status_list' => true,
        'label_count'               => _n_noop( 'Đang giao hàng <span class="count">(%s)</span>', 'Đang giao hàng <span class="count">(%s)</span>', 'woocommerce' ),
    );
    return $order_statuses;
}

// Thêm trạng thái vào chi tiết đơn hàng
add_filter( 'wc_order_statuses', 'custom_status_order' );
function custom_status_order( $order_statuses ) {
    $new_order_statuses = array();
    foreach ( $order_statuses as $key => $label ) {
        $new_order_statuses[ $key ] = $label;
        if ( 'wc-on-hold' === $key ) {
            $new_order_statuses['wc-danggiaohang'] = _x( 'Đang giao hàng', 'Order status', 'woocommerce' );
        }
    }
    return $new_order_statuses;
}

// Thêm vào bulk actions trang Đơn hàng
add_filter( 'bulk_actions-edit-shop_order', 'custom_bulk_status_order' );
function custom_bulk_status_order( $bulk_actions ) {
    $bulk_actions['mark_danggiaohang'] = 'Đổi trạng thái sang Đang giao hàng';
    return $bulk_actions;
}

// Thay đổi màu nhãn trạng thái
function add_admin_custom_css() {
    echo '<style>
        .order-status.status-danggiaohang {
            background: #d0e1c8;
            color: #026600;
        }
    </style>';
}
add_action('admin_head', 'add_admin_custom_css');

Ngoài ra thì mình cũng đổi tên 2 trạng thái mặc định cho phù hợp

  • Tạm giữ -> Đã nhận đơn
  • Đang thực hiện -> Đang chuẩn bị hàng
PHP
<?php

//Đổi tên 2 trạng thái mặc định
add_filter('wc_order_statuses', 'evps_rename_order_status');
function evps_rename_order_status($statuses)
{
    $statuses['wc-on-hold'] = 'Đã nhận đơn';
    $statuses['wc-processing'] = 'Đang chuẩn bị hàng';
    return $statuses;
}
add_filter( 'woocommerce_register_shop_order_post_statuses', 'evps_rename_order_status_counter' );
function evps_rename_order_status_counter( $statuses ) {
	$statuses['wc-on-hold']['label_count'] = _n_noop( 'Đã nhận đơn <span class="count">(%s)</span>', 'Đã nhận đơn <span class="count">(%s)</span>', 'woocommerce' );
	
	$statuses['wc-processing']['label_count'] = _n_noop( 'Đang chuẩn bị hàng <span class="count">(%s)</span>', 'Đang chuẩn bị hàng <span class="count">(%s)</span>', 'woocommerce' );
  return $statuses;
}

// Đổi tên trạng thái trong Bulk Actions trang danh sách Đơn hàng
add_filter('bulk_actions-edit-shop_order', 'evps_custom_bulk_actions');
function evps_custom_bulk_actions($bulk_actions)
{
    $bulk_actions['mark_on-hold'] = 'Chuyển sang đã nhận đơn';
    $bulk_actions['mark_processing'] = 'Chuyển sang đang chuẩn bị hàng';
    
    return $bulk_actions;
}
?>

Bước 2: Tạo form, thêm chức năng tìm đơn hàng theo số điện thoại

Code khá dài, mình có comment từng chỗ cho bạn hiểu, bạn có thể code mở rộng thêm chức năng tuỳ theo nhu cầu nhé.

PHP
<?php
// Tuỳ biến trang tra cứu thông tin đơn hàng
function custom_order_tracking_form() {
    ?>
    <div class="form-tracking-order">
        <form method="post" action="">
            <div class="wrapper">
                <div class="search-method-tabs">
					<div class="child-tab">
						<label class="boxed-check">
                        <input class="boxed-check-input" type="radio" name="search_method" value="phone" checked>
						<div class="boxed-check-label">Tìm theo số điện thoại</div>
                    </label>
					</div>
					<div class="child-tab">
						<label class="boxed-check">
                        <input class="boxed-check-input" type="radio" name="search_method" value="email">
						<div class="boxed-check-label">Tìm theo email</div>
                    </label>
					</div>
                </div>
                <div class="input-wrapper">
					<div class="input phone-input">
						<input type="tel" name="billing_phone" id="billing_phone" 
							   pattern="(0|\+84)(3[2-9]|5[2689]|7[0-9]|8[1-9]|9[0-9])\d{7}" 
							   placeholder="Nhập số điện thoại đặt hàng. VD: 0909654684"
							   title="Nhập số điện thoại hợp lệ của Việt Nam">
						<p class="warning-message" style="color:red;font-size:0.9rem;font-style:italic"></p>
					</div>

					<div class="input email-input" style="display:none;">
						<input type="email" name="billing_email" id="billing_email" 
							   placeholder="Nhập email đặt hàng. VD: thaikhang061091@gmail.com"
							   title="Vui lòng nhập email hợp lệ">
						<p class="warning-message" style="color:red;font-size:0.9rem;font-style:italic"></p>
					</div>
				</div>
                <div class="submit">
                    <input type="submit" name="track_order" value="Tra cứu đơn hàng">
                </div>
            </div>
        </form>
    </div>
        /* JS xử lý chuyển đổi giữa tìm theo sđt và email */
    <script>
      jQuery(document).ready(function($) {
    		$('input[name="search_method"]').on('change', function() {
    			if ($(this).val() === 'phone') {
    				$('.phone-input').show();
    				$('.email-input').hide();
    				$('#billing_phone').attr('required', '');
    				$('#billing_email').removeAttr('required');
    			} else {
    				$('.phone-input').hide();
    				$('.email-input').show();
    				$('#billing_email').attr('required', '');
    				$('#billing_phone').removeAttr('required');
    			}
    		});
    		$('#billing_phone').on('input', function() {
    			const $warningElement = $(this).next('.warning-message');
    			if ($(this).val() && !this.validity.valid) {
    				$warningElement.text('Vui lòng nhập số điện thoại hợp lệ');
    			} else {
    				$warningElement.text('');
    			}
    		});
    	});
    </script>

    <?php
    // Xử lý truy vấn đơn hàng
    if (isset($_POST['track_order'])) {
        $search_method = isset($_POST['search_method']) ? sanitize_text_field($_POST['search_method']) : 'phone';
        $search_value = '';
        $search_args = [];
        
        if ($search_method === 'phone' && !empty($_POST['billing_phone'])) {
            $search_value = sanitize_text_field($_POST['billing_phone']);
            $search_args = ['billing_phone' => $search_value];
        } elseif ($search_method === 'email' && !empty($_POST['billing_email'])) {
            $search_value = sanitize_email($_POST['billing_email']);
            $search_args = ['billing_email' => $search_value];
        } else {
            echo '<p style="text-align: center;">Vui lòng nhập thông tin tìm kiếm</p>';
            return;
        }
        
        // Tham số truy vấn theo thứ tự ngày mới nhất đến cũ nhất
        $search_args['orderby'] = 'date';
        $search_args['order'] = 'DESC';
        
        // Truy vấn cơ sở dữ liệu để lấy thông tin đặt hàng
        $orders = wc_get_orders($search_args);
        
        // Hiển thị thông báo
        if ($orders) {
            display_order_results($orders, $search_method, $search_value);
        } else {
            $search_type = ($search_method === 'phone') ? 'số điện thoại' : 'email';
            echo '<p style="text-align: center;">Không tìm thấy đơn hàng cho ' . $search_type . ' ' . esc_html($search_value) . '</p>';
        }
    }
}

Bước 3: Hiển thị đơn hàng theo từng tab trạng thái đơn hàng

PHP
// Hiển thị kết quả đơn hàng theo tab
function display_order_results($orders, $search_method, $search_value) {
    $orders_by_id = [];
    foreach ($orders as $order) {
        $orders_by_id[$order->get_id()] = $order;
    }
	
    $order_statuses = [
        'all-orders' => $orders,
        'on-hold' => [],
        'processing' => [],
        'danggiaohang' => [],
        'completed' => [],
        'cancelled' => [],
        'refunded' => [],
    ];
	
    $order_statuses['all-orders'] = $orders;
    
    foreach ($orders as $order) {
        $status = esc_attr($order->get_status());
        if (isset($order_statuses[$status])) {
            $order_statuses[$status][] = $order;
        }
    }

    $wc_statuses = wc_get_order_statuses();
	
    // Hiển thị các tab trạng thái đơn hàng
    echo '<ul class="tab-links">';
    $active_class = 'active';
    
	// Chỉ hiển thị label số lượng nếu có đơn hàng tab "tất cả"
	$count_html = count($orders) > 0 ? '<span class="tab-count absolute" data-count="' . count($orders) . '">' . count($orders) . '</span>' : '';
    echo '<li class="' . $active_class . ' wc-all-orders relative"><a href="#all-orders">Tất cả</a>' . $count_html . '</li>';
    
    // Các tab trạng thái khác
    foreach ($order_statuses as $status => $status_orders) {
        if ($status !== 'all-orders') {
            // Chỉ hiển thị label số lượng nếu có đơn hàng các tab khác
            $count_html = count($status_orders) > 0 ? '<span class="tab-count absolute" data-count="' . count($status_orders) . '">' . count($status_orders) . '</span>' : '';
            $tab_title = wc_get_order_status_name($status);
            echo '<li class="wc-' . $status . ' relative"><a href="#' . $status . '">' . $tab_title . '</a>' . $count_html . '</li>';
        }
    }
    echo '</ul>';
    
    // Hiển thị danh sách đơn hàng theo từng tab trạng thái
	foreach ($order_statuses as $status => $status_orders) {
        $active_class = ($status === 'all-orders') ? 'active' : '';
        echo '<div class="' . $active_class . ' wc-' . $status . ' wrapper-tracking-order">';
        
        if (count($status_orders) > 0) {
            foreach ($status_orders as $order) {
                display_single_order($order, $search_method, $search_value);
            }
        } else {
            // Hiển thị thông báo khi không có đơn hàng nào trong tab
            if ($status !== 'all-orders') {
                echo '<p style="text-align: center;">Không có đơn hàng nào ở trạng thái ' . wc_get_order_status_name($status) . '</p>';
            } else {
                echo '<p style="text-align: center;">Không có đơn hàng nào</p>';
            }
        }
        
        echo '</div>';
    }
    ?>
    /* JS cho phần chuyển tab */
 <script>
  jQuery(document).ready(function($) {
		$('.tab-links li a').on('click', function(e) {
			e.preventDefault();
			$('.tab-links li').removeClass('active');
			$('.wrapper-tracking-order').removeClass('active');
			$(this).parent().addClass('active');
			const tabId = $(this).attr('href').substring(1);
			$('.wc-' + tabId + '.wrapper-tracking-order').addClass('active');
		});
	});
    </script>
    <?php
}

Bước 4 : Hiển thị thông tin đơn hàng

PHP
// Hiển thị thông tin chi tiết của một đơn hàng

function display_single_order($order, $search_method, $search_value)
{
    // Ngày đặt hàng và ngày dự kiến giao hàng vào biến
    $order_date = $order->get_date_created();
    $estimated_delivery_date = clone $order_date;
    $estimated_delivery_date->modify('+2 days'); // Chỗ này mình đang để thời gian dự kiến giao hàng là + thêm 2 ngày tính từ ngày đặt hàng

?>
    <div class="thong-tin-tra-cuu col petzen-row" style="padding: 1.5rem; border: 1px solid #eee; border-radius: 8px;">
        <div class="order-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
            <h3 style="font-size:1.2rem!important; margin: 0;"> Đơn hàng: <?php echo esc_html($order->get_id()); ?></h3>
            <?php
            $order_status = esc_attr($order->get_status());
            $status_name = esc_html(wc_get_order_status_name($order_status));
            $status_class = 'status-' . esc_html($order_status);
            echo '<span class="order-tracking ' . esc_attr($status_class) . '">' . esc_html($status_name) . '</span>';
            ?>
        </div>

        <table class="woocommerce-table woocommerce-table--order-details shop_table order_details">
            <tbody>
                <?php
                $total_items = 0;
                foreach ($order->get_items() as $item) {
                    $product = $item->get_product();
                    $quantity = $item->get_quantity();
                    $price = $item->get_total();
                    $total_items += $quantity;

                    // Kiểm tra nếu sản phẩm tồn tại
                    if ($product) {
                        $product_image = wp_get_attachment_image_src($product->get_image_id(), 'thumbnail');
                        $image_url = $product_image ? esc_url($product_image[0]) : wc_placeholder_img_src('thumbnail');
                        $product_permalink = $product->get_permalink();
                    } else {
                        $image_url = wc_placeholder_img_src('thumbnail');
                        $product_permalink = '#';
                    }
                ?>
                    <tr class="woocommerce-table__line-item order_item">
                        <td class="woocommerce-table__product-name product-name" style="display:flex;align-items:center;">
                            <div class="item-thumbnail" style="float:left;margin-right:10px">
                                <img width="50" height="50" style="border: 2px dashed #b0d3ef;border-radius: 8px;padding: 5px;"
                                    src="<?php echo $image_url; ?>" class="attachment-70x70 size-70x70 lazy-load-active"
                                    alt="<?php echo esc_html($item->get_name()); ?>" title="<?php echo esc_html($item->get_name()); ?>">
                            </div>
                            <a href="<?php echo $product_permalink; ?>"><?php echo esc_html($item->get_name()); ?></a>
                            <strong class="product-quantity">   x <?php echo esc_html($quantity); ?></strong>
                        </td>
                        <td class="woocommerce-table__product-total product-total">
                            <span class="woocommerce-Price-amount amount"><?php echo wc_price($price); ?></span>
                        </td>
                    </tr>
                <?php } ?>
                <tr class="order-total">
                    <td colspan="1"><strong>Tổng cộng:</strong> (<?php echo $total_items; ?> sản phẩm)</td>
                    <td><strong><?php echo wc_price($order->get_total()); ?></strong></td>
                </tr>
                <tr>
                    <td colspan="2" style="font-size:0.9rem; color:gray;text-align:left">
                        <div>Ngày đặt hàng: <?php echo $order_date->format('d-m-Y H:i'); ?></div>
                        <div>Dự kiến giao hàng: <?php echo $estimated_delivery_date->format('d-m-Y'); ?></div>
                    </td>
                </tr>
            </tbody>
        </table>

        <div class="order-details-container" style="display: flex; flex-wrap: wrap; gap: 2rem; margin-top: 1rem;">
            <div class="info-customer" style="font-size:0.9rem; flex: 1; min-width: 250px;">
                <h4 style="font-size: 1rem!important; margin-bottom: 0.5rem;font-weight: 600;">Thông tin người đặt</h4>
					<ul>
						<li style="margin-bottom:0.3rem">Họ  tên: <?php echo esc_html($order->get_billing_last_name()); ?> /* Chỗ này do mình đang tuỳ biến cho khách điền họ tên trong 1 trường last name nên chỉ lấy last name,bạn có thể thay bằng "echo esc_html($order->get_billing_first_name() . ' ' . $order->get_billing_last_name());" để lấy đủ họ tên */
            </li>
						<li style="margin-bottom:0.3rem">Số điện thoại: <?php echo esc_html($order->get_billing_phone()); ?></li>
						<li style="margin-bottom:0.3rem">Email: <?php echo esc_html($order->get_billing_email()); ?></li>
					</ul>
            </div>

            <div class="shipping-info" style="font-size:0.9rem; flex: 1; min-width: 250px;">
                 <h4 style="font-size: 1rem!important; margin-bottom: 0.5rem;font-weight: 600;">Địa chỉ nhận hàng</h4>
                <?php
                $shipping_address = $order->get_address('shipping');
                $billing_address = $order->get_address('billing');
                $address_parts = [];

                // Lấy địa chỉ nhận hàng từ shipping hoặc billing
                if (!empty($shipping_address['address_1']) || !empty($billing_address['address_1'])) {
                    $address_parts[] = !empty($shipping_address['address_1']) ? $shipping_address['address_1'] : $billing_address['address_1'];
                }
                if (!empty($shipping_address['address_2']) || !empty($billing_address['address_2'])) {
                    $address_parts[] = !empty($shipping_address['address_2']) ? $shipping_address['address_2'] : $billing_address['address_2'];
                }
                if (!empty($shipping_address['city']) || !empty($billing_address['city'])) {
                    $address_parts[] = !empty($shipping_address['city']) ? $shipping_address['city'] : $billing_address['city'];
                }
                if (!empty($shipping_address['state']) || !empty($billing_address['state'])) {
                    $address_parts[] = !empty($shipping_address['state']) ? $shipping_address['state'] : $billing_address['state'];
                }
                echo '<ul>
						<li style="margin-bottom:0.3rem">' . implode(', ', $address_parts) . '</li>
					  </ul>';
                ?>

                <h4 style="font-size: 1rem!important; margin-bottom: 0.5rem;font-weight: 600;">Phương thức thanh toán</h4>
					<ul>
						<li style="margin-bottom:0.3rem"><?php echo $order->get_payment_method_title(); ?></li>
					</ul>

                <?php if ($order->get_customer_note()) : ?>
                    <h4 style="font-size: 1rem!important; margin-bottom: 0.5rem;font-weight: 600;">Ghi chú</h4>
                    <ul>
						<li style="margin-bottom:0.3rem"><?php echo esc_html($order->get_customer_note()); ?></li>
					</ul>
                <?php endif; ?>
            </div>
        </div>
     </div>   
<?php
}

add_shortcode('order_tracking_form', 'custom_order_tracking_form');

Bước 5: Thêm CSS

Phần này bạn tuỳ chỉnh lại cho phù hợp với giao diện nhé. Thêm vào file style.css trong child-theme hoặc phần CSS tuỳ chỉnh mặc định của WordPress để hiệu quả nhất nhé.

CSS
/*----- TRACKING ORDERS -------*/
.search-method-tabs {
	display:flex;
	align-content:center;
	justify-content:center;
	gap:20px;
	margin-bottom:15px;
}
.form-tracking-order{
	text-align:center;
	margin-top:80px;
	margin-bottom: 80px;
}
.form-tracking-order .input-wrapper .input input{
	min-height:60px!important;
	border-radius:10px;
	max-width: 600px;
  width: 100%;
}
.form-tracking-order .input-wrapper .input input{
	text-align:center;
}
.form-tracking-order input[type="submit"]{
	min-height:50px!important;
	padding:0 30px;
	margin-top:30px!important;
}
.boxed-check .boxed-check-input {
    -webkit-appearance: none;
    -moz-appearance: none;
    display: none;
}
.boxed-check .boxed-check-input:checked + .boxed-check-label{
	color: #ffffff;
  background-color: var(--third-color);
}

.boxed-check .boxed-check-label {
	display: block;
  background-color: #ffffff;
  padding: .7rem 1.5rem;
  margin: 0;
  border-radius: 10px;
  cursor: pointer;
	font-weight:normal
}
.page-id-133 h1{text-align:center!important}

ul.tab-links {
  background: white;
  padding: 1rem;
  border-radius: 10px;
	overflow-x: scroll;
  display: flex;
}
@media (min-width:900px){
ul.tab-links {
	overflow-x: unset;
}
}
.tab-links li {
	min-width:max-content;
	display: inline-block;
  margin-right: 10px;
  cursor: pointer;
  padding: 5px 10px;
  margin-left: 0!important;
  margin-right: 13px;
	margin-bottom:0!important;
}
.tab-links li a{
  text-decoration: none;
	color:#3b3b3f;
}
.tab-links :is(li.active a, li:hover a) {
  color: #ef7878;
}
span.tab-count {
    top: -1px;
    right: -5px;
    background: #fb3535;
    padding: 6px;
    border-radius: 90px;
    width: 17px;
    height: 17px;
    color: white;
    text-align: center;
    line-height: 5px;
    font-size: 0.7rem;
    box-shadow: 0 2px 6px #35323247;
}
@media(min-width:820.1px){
.thong-tin-tra-cuu .order-tracking{
	top: 20px;
  right: 21px;
  padding: 0.5rem 1rem;
  border-radius: 90px;
  font-size: .9rem;
	position:absolute;
}
.wrapper-tracking-order{
		display:none;
		grid-template-columns:repeat(2,1fr);
		gap:30px;
	}
}
@media(max-width:820px){
.search-method-tabs{
		flex-direction:column;
		gap:10px;
	}
.thong-tin-tra-cuu .order-tracking{
	position:relative;
	top:unset;
	right:unset;
	padding: 0.3rem 1rem;
  border-radius: 90px;
  font-size: .8rem;
	min-width: max-content;
  text-align: center;
}
.wrapper-tracking-order{
		display:none;
		grid-template-columns:auto;
		gap:30px;
}
.wrapper-tracking-order table{
		margin-top:15px;
	}
}
.wrapper-tracking-order.active{
		display:grid;
}
.thong-tin-tra-cuu .order-tracking {
  border-width: 1px;
  border-style: solid;
}

.thong-tin-tra-cuu .order-tracking.status-on-hold,
.thong-tin-tra-cuu .order-tracking.status-processing {
  background: #beffaf;
  color: #308b1c;
  border-color: #308b1c;
}

.thong-tin-tra-cuu .order-tracking.status-danhandon {
  background: #e2fffa;
  color: #29b8a1;
  border-color: #29b8a1;
}

.thong-tin-tra-cuu .order-tracking.status-danggiaohang {
  background: #ffe8f0;
  color: #bf017d;
  border-color: #bf017d;
}

.thong-tin-tra-cuu .order-tracking.status-khonggiaoduochang {
  background: #fff3e8;
  color: #e47916;
  border-color: #e47916;
}

.thong-tin-tra-cuu .order-tracking.status-completed {
  background: #e8f2ff;
  color: #1859a6;
  border-color: #1859a6;
}

.thong-tin-tra-cuu .order-tracking.status-cancelled {
  background: #e6e6e6;
  color: #36383a;
  border-color: #36383a;
}
/*------ END TRACKING ORDERS -------*/

Toàn bộ code PHP từ bước 2 Tạo form, thêm chức năng tìm đơn hàng theo số điện thoại

PHP
<?php
// Tuỳ biến trang tra cứu thông tin đơn hàng
function custom_order_tracking_form() {
    ?>
    <div class="form-tracking-order">
        <form method="post" action="">
            <div class="wrapper">
                <div class="search-method-tabs">
					<div class="child-tab">
						<label class="boxed-check">
                        <input class="boxed-check-input" type="radio" name="search_method" value="phone" checked>
						<div class="boxed-check-label">Tìm theo số điện thoại</div>
                    </label>
					</div>
					<div class="child-tab">
						<label class="boxed-check">
                        <input class="boxed-check-input" type="radio" name="search_method" value="email">
						<div class="boxed-check-label">Tìm theo email</div>
                    </label>
					</div>
                </div>
                <div class="input-wrapper">
					<div class="input phone-input">
						<input type="tel" name="billing_phone" id="billing_phone" 
							   pattern="(0|\+84)(3[2-9]|5[2689]|7[0-9]|8[1-9]|9[0-9])\d{7}" 
							   placeholder="Nhập số điện thoại đặt hàng. VD: 0909654684"
							   title="Nhập số điện thoại hợp lệ của Việt Nam">
						<p class="warning-message" style="color:red;font-size:0.9rem;font-style:italic"></p>
					</div>

					<div class="input email-input" style="display:none;">
						<input type="email" name="billing_email" id="billing_email" 
							   placeholder="Nhập email đặt hàng. VD: thaikhang061091@gmail.com"
							   title="Vui lòng nhập email hợp lệ">
						<p class="warning-message" style="color:red;font-size:0.9rem;font-style:italic"></p>
					</div>
				</div>
                <div class="submit">
                    <input type="submit" name="track_order" value="Tra cứu đơn hàng">
                </div>
            </div>
        </form>
    </div>
        /* JS xử lý chuyển đổi giữa tìm theo sđt và email */
    <script>
      jQuery(document).ready(function($) {
    		$('input[name="search_method"]').on('change', function() {
    			if ($(this).val() === 'phone') {
    				$('.phone-input').show();
    				$('.email-input').hide();
    				$('#billing_phone').attr('required', '');
    				$('#billing_email').removeAttr('required');
    			} else {
    				$('.phone-input').hide();
    				$('.email-input').show();
    				$('#billing_email').attr('required', '');
    				$('#billing_phone').removeAttr('required');
    			}
    		});
    		$('#billing_phone').on('input', function() {
    			const $warningElement = $(this).next('.warning-message');
    			if ($(this).val() && !this.validity.valid) {
    				$warningElement.text('Vui lòng nhập số điện thoại hợp lệ');
    			} else {
    				$warningElement.text('');
    			}
    		});
    	});
    </script>

    <?php
    // Xử lý truy vấn đơn hàng
    if (isset($_POST['track_order'])) {
        $search_method = isset($_POST['search_method']) ? sanitize_text_field($_POST['search_method']) : 'phone';
        $search_value = '';
        $search_args = [];
        
        if ($search_method === 'phone' && !empty($_POST['billing_phone'])) {
            $search_value = sanitize_text_field($_POST['billing_phone']);
            $search_args = ['billing_phone' => $search_value];
        } elseif ($search_method === 'email' && !empty($_POST['billing_email'])) {
            $search_value = sanitize_email($_POST['billing_email']);
            $search_args = ['billing_email' => $search_value];
        } else {
            echo '<p style="text-align: center;">Vui lòng nhập thông tin tìm kiếm</p>';
            return;
        }
        
        // Tham số truy vấn theo thứ tự ngày mới nhất đến cũ nhất
        $search_args['orderby'] = 'date';
        $search_args['order'] = 'DESC';
        
        // Truy vấn cơ sở dữ liệu để lấy thông tin đặt hàng
        $orders = wc_get_orders($search_args);
        
        // Hiển thị thông báo
        if ($orders) {
            display_order_results($orders, $search_method, $search_value);
        } else {
            $search_type = ($search_method === 'phone') ? 'số điện thoại' : 'email';
            echo '<p style="text-align: center;">Không tìm thấy đơn hàng cho ' . $search_type . ' ' . esc_html($search_value) . '</p>';
        }
    }
}
// Hiển thị kết quả đơn hàng theo tab
function display_order_results($orders, $search_method, $search_value) {
    $orders_by_id = [];
    foreach ($orders as $order) {
        $orders_by_id[$order->get_id()] = $order;
    }
	
    $order_statuses = [
        'all-orders' => $orders,
        'on-hold' => [],
        'processing' => [],
        'danggiaohang' => [],
        'completed' => [],
        'cancelled' => [],
        'refunded' => [],
    ];
	
    $order_statuses['all-orders'] = $orders;
    
    foreach ($orders as $order) {
        $status = esc_attr($order->get_status());
        if (isset($order_statuses[$status])) {
            $order_statuses[$status][] = $order;
        }
    }

    $wc_statuses = wc_get_order_statuses();
	
    // Hiển thị các tab trạng thái đơn hàng
    echo '<ul class="tab-links">';
    $active_class = 'active';
    
	// Chỉ hiển thị label số lượng nếu có đơn hàng tab "tất cả"
	$count_html = count($orders) > 0 ? '<span class="tab-count absolute" data-count="' . count($orders) . '">' . count($orders) . '</span>' : '';
    echo '<li class="' . $active_class . ' wc-all-orders relative"><a href="#all-orders">Tất cả</a>' . $count_html . '</li>';
    
    // Các tab trạng thái khác
    foreach ($order_statuses as $status => $status_orders) {
        if ($status !== 'all-orders') {
            // Chỉ hiển thị label số lượng nếu có đơn hàng các tab khác
            $count_html = count($status_orders) > 0 ? '<span class="tab-count absolute" data-count="' . count($status_orders) . '">' . count($status_orders) . '</span>' : '';
            $tab_title = wc_get_order_status_name($status);
            echo '<li class="wc-' . $status . ' relative"><a href="#' . $status . '">' . $tab_title . '</a>' . $count_html . '</li>';
        }
    }
    echo '</ul>';
    
    // Hiển thị danh sách đơn hàng theo từng tab trạng thái
	foreach ($order_statuses as $status => $status_orders) {
        $active_class = ($status === 'all-orders') ? 'active' : '';
        echo '<div class="' . $active_class . ' wc-' . $status . ' wrapper-tracking-order">';
        
        if (count($status_orders) > 0) {
            foreach ($status_orders as $order) {
                display_single_order($order, $search_method, $search_value);
            }
        } else {
            // Hiển thị thông báo khi không có đơn hàng nào trong tab
            if ($status !== 'all-orders') {
                echo '<p style="text-align: center;">Không có đơn hàng nào ở trạng thái ' . wc_get_order_status_name($status) . '</p>';
            } else {
                echo '<p style="text-align: center;">Không có đơn hàng nào</p>';
            }
        }
        
        echo '</div>';
    }
    ?>
    /* JS cho phần chuyển tab */
 <script>
  jQuery(document).ready(function($) {
		$('.tab-links li a').on('click', function(e) {
			e.preventDefault();
			$('.tab-links li').removeClass('active');
			$('.wrapper-tracking-order').removeClass('active');
			$(this).parent().addClass('active');
			const tabId = $(this).attr('href').substring(1);
			$('.wc-' + tabId + '.wrapper-tracking-order').addClass('active');
		});
	});
    </script>
    <?php
}

// Hiển thị thông tin chi tiết của một đơn hàng

function display_single_order($order, $search_method, $search_value)
{
    // Ngày đặt hàng và ngày dự kiến giao hàng vào biến
    $order_date = $order->get_date_created();
    $estimated_delivery_date = clone $order_date;
    $estimated_delivery_date->modify('+2 days'); // Chỗ này mình đang để thời gian dự kiến giao hàng là + thêm 2 ngày tính từ ngày đặt hàng

?>
    <div class="thong-tin-tra-cuu col petzen-row" style="padding: 1.5rem; border: 1px solid #eee; border-radius: 8px;">
        <div class="order-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
            <h3 style="font-size:1.2rem!important; margin: 0;"> Đơn hàng: <?php echo esc_html($order->get_id()); ?></h3>
            <?php
            $order_status = esc_attr($order->get_status());
            $status_name = esc_html(wc_get_order_status_name($order_status));
            $status_class = 'status-' . esc_html($order_status);
            echo '<span class="order-tracking ' . esc_attr($status_class) . '">' . esc_html($status_name) . '</span>';
            ?>
        </div>

        <table class="woocommerce-table woocommerce-table--order-details shop_table order_details">
            <tbody>
                <?php
                $total_items = 0;
                foreach ($order->get_items() as $item) {
                    $product = $item->get_product();
                    $quantity = $item->get_quantity();
                    $price = $item->get_total();
                    $total_items += $quantity;

                    // Kiểm tra nếu sản phẩm tồn tại
                    if ($product) {
                        $product_image = wp_get_attachment_image_src($product->get_image_id(), 'thumbnail');
                        $image_url = $product_image ? esc_url($product_image[0]) : wc_placeholder_img_src('thumbnail');
                        $product_permalink = $product->get_permalink();
                    } else {
                        $image_url = wc_placeholder_img_src('thumbnail');
                        $product_permalink = '#';
                    }
                ?>
                    <tr class="woocommerce-table__line-item order_item">
                        <td class="woocommerce-table__product-name product-name" style="display:flex;align-items:center;">
                            <div class="item-thumbnail" style="float:left;margin-right:10px">
                                <img width="50" height="50" style="border: 2px dashed #b0d3ef;border-radius: 8px;padding: 5px;"
                                    src="<?php echo $image_url; ?>" class="attachment-70x70 size-70x70 lazy-load-active"
                                    alt="<?php echo esc_html($item->get_name()); ?>" title="<?php echo esc_html($item->get_name()); ?>">
                            </div>
                            <a href="<?php echo $product_permalink; ?>"><?php echo esc_html($item->get_name()); ?></a>
                            <strong class="product-quantity">   x <?php echo esc_html($quantity); ?></strong>
                        </td>
                        <td class="woocommerce-table__product-total product-total">
                            <span class="woocommerce-Price-amount amount"><?php echo wc_price($price); ?></span>
                        </td>
                    </tr>
                <?php } ?>
                <tr class="order-total">
                    <td colspan="1"><strong>Tổng cộng:</strong> (<?php echo $total_items; ?> sản phẩm)</td>
                    <td><strong><?php echo wc_price($order->get_total()); ?></strong></td>
                </tr>
                <tr>
                    <td colspan="2" style="font-size:0.9rem; color:gray;text-align:left">
                        <div>Ngày đặt hàng: <?php echo $order_date->format('d-m-Y H:i'); ?></div>
                        <div>Dự kiến giao hàng: <?php echo $estimated_delivery_date->format('d-m-Y'); ?></div>
                    </td>
                </tr>
            </tbody>
        </table>

        <div class="order-details-container" style="display: flex; flex-wrap: wrap; gap: 2rem; margin-top: 1rem;">
            <div class="info-customer" style="font-size:0.9rem; flex: 1; min-width: 250px;">
                <h4 style="font-size: 1rem!important; margin-bottom: 0.5rem;font-weight: 600;">Thông tin người đặt</h4>
					<ul>
						<li style="margin-bottom:0.3rem">Họ  tên: <?php echo esc_html($order->get_billing_last_name()); ?> /* Chỗ này do mình đang tuỳ biến cho khách điền họ tên trong 1 trường last name nên chỉ lấy last name,bạn có thể thay bằng "echo esc_html($order->get_billing_first_name() . ' ' . $order->get_billing_last_name());" để lấy đủ họ tên */
            </li>
						<li style="margin-bottom:0.3rem">Số điện thoại: <?php echo esc_html($order->get_billing_phone()); ?></li>
						<li style="margin-bottom:0.3rem">Email: <?php echo esc_html($order->get_billing_email()); ?></li>
					</ul>
            </div>

            <div class="shipping-info" style="font-size:0.9rem; flex: 1; min-width: 250px;">
                 <h4 style="font-size: 1rem!important; margin-bottom: 0.5rem;font-weight: 600;">Địa chỉ nhận hàng</h4>
                <?php
                $shipping_address = $order->get_address('shipping');
                $billing_address = $order->get_address('billing');
                $address_parts = [];

                // Lấy địa chỉ nhận hàng từ shipping hoặc billing
                if (!empty($shipping_address['address_1']) || !empty($billing_address['address_1'])) {
                    $address_parts[] = !empty($shipping_address['address_1']) ? $shipping_address['address_1'] : $billing_address['address_1'];
                }
                if (!empty($shipping_address['address_2']) || !empty($billing_address['address_2'])) {
                    $address_parts[] = !empty($shipping_address['address_2']) ? $shipping_address['address_2'] : $billing_address['address_2'];
                }
                if (!empty($shipping_address['city']) || !empty($billing_address['city'])) {
                    $address_parts[] = !empty($shipping_address['city']) ? $shipping_address['city'] : $billing_address['city'];
                }
                if (!empty($shipping_address['state']) || !empty($billing_address['state'])) {
                    $address_parts[] = !empty($shipping_address['state']) ? $shipping_address['state'] : $billing_address['state'];
                }
                echo '<ul>
						<li style="margin-bottom:0.3rem">' . implode(', ', $address_parts) . '</li>
					  </ul>';
                ?>

                <h4 style="font-size: 1rem!important; margin-bottom: 0.5rem;font-weight: 600;">Phương thức thanh toán</h4>
					<ul>
						<li style="margin-bottom:0.3rem"><?php echo $order->get_payment_method_title(); ?></li>
					</ul>

                <?php if ($order->get_customer_note()) : ?>
                    <h4 style="font-size: 1rem!important; margin-bottom: 0.5rem;font-weight: 600;">Ghi chú</h4>
                    <ul>
						<li style="margin-bottom:0.3rem"><?php echo esc_html($order->get_customer_note()); ?></li>
					</ul>
                <?php endif; ?>
            </div>
        </div>
     </div>   
<?php
}

add_shortcode('order_tracking_form', 'custom_order_tracking_form');

Sử dụng Shortcode

Thêm shortcode vào trang theo dõi đơn hàng của woocommerce.

[order_tracking_form]

Kết luận

Vậy là hoàn tất các bước để tạo 1 trang theo dõi đơn hàng bằng số điện thoại trong Woocommerce mà vẫn giữ được tính năng theo dõi đơn hàng bằng email, chúc các bạn thành công và hài lòng nhé.

Code khá nhiều và mình phải sửa lại code của mình rất nhiều khi chia sẻ với bạn để phù hợp nhất với website cơ bản, phần còn lại để các bạn tuỳ biến theo nhu cầu cá nhân. Nếu có gì sai sót hay thắc mắc thì đừng ngại nhắn cho mình nhé.

Nếu có nhu cầu mua Hosting và VPS các bạn ủng hộ mình nhé:

Bài viết khác