Lesson 28 — Adding Sortable Columns to the Auctions Table Using WP_List_Table

Series: Building the Flipnzee Auctions WordPress Plugin
Lesson: 28
Difficulty: Intermediate


Introduction

In the previous lessons, we transformed our Auctions table into a professional management interface by adding pagination and functional search.

However, one important feature is still missing.

When managing many auctions, administrators often want to sort records by clicking column headings, just like they can on the WordPress Posts, Pages, Users, and Comments screens.

In this lesson, we’ll add sortable columns to our custom WP_List_Table, allowing administrators to sort auctions with a single click.


What You’ll Learn

By the end of this lesson, you’ll know how to:

  • Register sortable columns in WP_List_Table
  • Detect the selected sort column
  • Detect ascending and descending order
  • Build secure dynamic SQL queries
  • Keep sorting compatible with pagination and search
  • Create a much more professional admin interface

Why Sortable Columns Matter

Imagine managing hundreds of auctions.

Sometimes you may want to:

  • View the newest auctions first
  • Sort by Listing ID
  • Find the highest Buy Now price
  • Group auctions by Status
  • Review the lowest Start Price

Without sortable columns, administrators must manually search through multiple pages.

Sorting makes large datasets much easier to manage.


How WordPress Sorting Works

When a column header is clicked, WordPress automatically appends two URL parameters.

Example:

admin.php?page=flipnzee-all-auctions&orderby=start_price&order=asc

or

admin.php?page=flipnzee-all-auctions&orderby=status&order=desc

Your plugin simply needs to read these values and use them safely.


Step 1 — Register Sortable Columns

Inside your table class, implement:

public function get_sortable_columns() {

	return array(
		'id'            => array( 'id', true ),
		'listing_id'    => array( 'listing_id', false ),
		'start_price'   => array( 'start_price', false ),
		'reserve_price' => array( 'reserve_price', false ),
		'buy_now_price' => array( 'buy_now_price', false ),
		'status'        => array( 'status', false ),
		'created_at'    => array( 'created_at', false ),
	);
}

This tells WordPress which columns are sortable.


Step 2 — Read Sorting Parameters

Inside prepare_items(), retrieve the selected column:

$orderby = isset( $_GET['orderby'] )
	? sanitize_key( $_GET['orderby'] )
	: 'created_at';

Next, retrieve the direction:

$order = isset( $_GET['order'] )
	? strtoupper(
		sanitize_text_field(
			wp_unslash( $_GET['order'] )
		)
	)
	: 'DESC';

Step 3 — Whitelist Allowed Columns

Never trust user input directly.

Instead, create an allowed list:

$allowed = array(
	'id',
	'listing_id',
	'start_price',
	'reserve_price',
	'buy_now_price',
	'status',
	'created_at',
);

If an invalid column is supplied, fall back to:

created_at

This prevents SQL injection.


Step 4 — Update the Database Query

Modify the Auction Manager so get_all_auctions() also accepts:

$orderby

and

$order

The SQL becomes:

ORDER BY created_at DESC

or

ORDER BY start_price ASC

depending on the administrator’s selection.


Step 5 — Keep Search and Pagination Working

Sorting should work together with:

  • Search
  • Pagination

All three features should operate simultaneously.

For example:

  • Search for draft
  • Sort by Start Price
  • Navigate to Page 2

Everything should continue working correctly.


Step 6 — Test Every Column

Click each heading:

  • ID
  • Listing ID
  • Start Price
  • Reserve Price
  • Buy Now Price
  • Status
  • Created At

Verify that clicking once sorts ascending.

Clicking again sorts descending.


Security Considerations

Because column names cannot be parameterized using $wpdb->prepare(), always whitelist allowed columns before inserting them into SQL.

Never allow arbitrary column names supplied by users.

Continue sanitizing all URL parameters using:

  • sanitize_key()
  • sanitize_text_field()
  • wp_unslash()

This keeps the plugin secure while supporting dynamic sorting.


Expected Result

After completing this lesson, administrators will be able to:

  • ✅ Click any supported column heading
  • ✅ Sort ascending or descending
  • ✅ Combine sorting with searching
  • ✅ Combine sorting with pagination
  • ✅ Manage auctions much more efficiently

The Auctions page will now behave almost identically to the native WordPress administration tables.


Conclusion

Adding sortable columns is another significant milestone in the development of the Flipnzee Auctions plugin.

Together with pagination and search, this feature creates a much richer administration experience and brings the plugin closer to production quality.

At this stage, the Auctions table supports the three most important data management features expected in professional WordPress plugins.


In the Next Lesson

We’ll implement bulk actions, allowing administrators to select multiple auctions using checkboxes and perform operations such as:

  • Delete Selected Auctions
  • Activate Multiple Auctions
  • Close Multiple Auctions

Bulk actions are one of the final major features needed before the Auctions management screen reaches enterprise-level usability.

Lesson 27 Implementation — Adding Functional Search to the Flipnzee Auctions Table

Implementation Series: Building the Flipnzee Auctions Plugin Step by Step
Lesson: 27 (Implementation)
Prerequisites: Lessons 25 and 26 completed


Introduction

In Lesson 26, the Search Auctions box was added above the auctions table using WordPress’ built-in search_box() method. Although the interface looked complete, typing a keyword and clicking Search Auctions did not actually filter any records.

In this implementation lesson, the search box is connected to the database so administrators can quickly find auctions by Auction ID, Listing ID, or Status.

After completing this implementation, the search feature behaves much like the search functionality found on WordPress’ built-in Posts and Pages screens.


What Was Implemented

This lesson introduced the following improvements:

  • Added support for search keywords
  • Filtered database results using SQL
  • Used secure SQL queries with $wpdb->prepare()
  • Escaped wildcard characters using $wpdb->esc_like()
  • Updated pagination so it counts only matching results
  • Preserved the search keyword after searching

Step 1 — Update get_all_auctions()

Open:

includes/class-auction-manager.php

Locate the get_all_auctions() method.

Originally it accepted only pagination values:

public static function get_all_auctions(
	$per_page = 20,
	$offset = 0
)

Add a third parameter:

public static function get_all_auctions(
	$per_page = 20,
	$offset = 0,
	$search = ''
)

This parameter will receive the administrator’s search keyword.


Step 2 — Modify the SQL Query

Previously, every auction was returned regardless of the search box.

The method was updated to check whether a search keyword exists.

If a keyword is present:

  • Create a safe LIKE pattern using $wpdb->esc_like()
  • Filter records using WHERE
  • Return only matching auctions

Otherwise, return all auctions exactly as before.

This keeps the plugin efficient while remaining secure.


Step 3 — Read the Search Keyword

Open:

admin/class-auctions-table.php

Inside prepare_items(), retrieve the keyword submitted by WordPress.

The search box automatically sends its value using:

$_REQUEST['s']

The keyword was safely sanitized using:

  • wp_unslash()
  • sanitize_text_field()

This protects the plugin from unsafe input.


Step 4 — Pass the Search Value

Previously, auctions were loaded like this:

Flipnzee_Auction_Manager::get_all_auctions(
	$per_page,
	$offset
);

The search variable was added as the third parameter:

Flipnzee_Auction_Manager::get_all_auctions(
	$per_page,
	$offset,
	$search
);

Now the database query knows what the administrator searched for.


Step 5 — Update Pagination

Searching introduced one small problem.

Although only matching auctions appeared, pagination still counted every auction in the database.

To solve this:

The count_auctions() method was updated to accept the same search keyword.

Instead of counting every record:

SELECT COUNT(*)

the query now counts only matching auctions whenever a search is active.

This keeps pagination accurate.


Step 6 — Update the Table

The table’s total item count was changed from:

Flipnzee_Auction_Manager::count_auctions();

to:

Flipnzee_Auction_Manager::count_auctions(
	$search
);

This small change synchronizes the search results with the page numbers.


Step 7 — Test the Feature

Several searches were performed during implementation.

Examples included:

  • Auction ID
  • Listing ID
  • Auction Status

The table correctly displayed only the matching auctions.

Pagination also updated automatically.

For example:

Searching for:

24

displayed:

  • 1 matching auction
  • 1 item
  • No additional pages

This confirmed that both searching and pagination were working together correctly.


Security Improvements

Several WordPress security functions were used throughout this lesson.

These included:

  • sanitize_text_field()
  • wp_unslash()
  • $wpdb->prepare()
  • $wpdb->esc_like()

Using these functions helps protect the plugin against SQL injection and unsafe user input.


Troubleshooting

During implementation, one issue appeared.

The search results filtered correctly, but pagination still showed the total number of auctions.

The cause was simple.

The table was still calling:

count_auctions();

instead of:

count_auctions( $search );

After updating this single line, pagination immediately displayed the correct number of matching results.


Result

At the end of this lesson, the Flipnzee Auctions plugin now supports:

  • ✅ Professional search box
  • ✅ Secure database searching
  • ✅ Pagination integrated with search
  • ✅ Accurate item counts
  • ✅ WordPress coding standards

The Auctions screen now behaves much more like WordPress’ own administration pages.


Conclusion

This implementation transformed the search box from a simple interface element into a fully functional administrative tool.

Combined with pagination from the previous lesson, administrators can now quickly locate auctions even when the database grows significantly.

The Flipnzee Auctions plugin continues to evolve from a learning project into a production-ready WordPress plugin.


Download Files

The source code for this lesson is available below:

  • Starting Project: Download the plugin before implementing Lesson 27.
⬇ Download Plugin (After Lesson 26) (0 downloads )
  • Completed Project: Download the updated plugin after implementing the functional search feature and compare your changes if needed.
⬇ Download Plugin (After Lesson 27) (0 downloads )

In the next implementation lesson, we’ll add sortable columns, allowing administrators to click table headers such as ID, Listing ID, Status, and Created Date to sort auction records just like the native WordPress admin screens.

Lesson 26 Implementation — Adding a Search Box to the Auctions Table (Step-by-Step)

Plugin: Flipnzee Auctions
Implementation Stage: Lesson 26
Difficulty: Intermediate
Prerequisites: Lessons 1–25 completed


Introduction

In Lesson 26, we improved the usability of the All Auctions page by adding a search box to the auctions table. WordPress’ WP_List_Table class includes a built-in search box method, allowing administrators to quickly search records without creating a custom user interface.

During implementation, we also encountered and resolved a PHP syntax error that prevented the plugin from activating. This demonstrates why validating PHP files after every change is an important part of plugin development.


Objective

By the end of this implementation, the plugin will:

  • Display a professional search box above the auctions table.
  • Continue supporting pagination.
  • Prepare the plugin for full search functionality in the next implementation step.

Step 1 — Open the Admin Page File

Open:

admin/class-admin.php

Locate the following method:

public function all_auctions_page() {

This method is responsible for displaying the All Auctions page inside the WordPress admin area.


Step 2 — Wrap the Table Inside a Form

Replace the existing table output with a GET form:

<form method="get">

	<input
		type="hidden"
		name="page"
		value="flipnzee-all-auctions"
	>

	<?php

	$table->search_box(
		'Search Auctions',
		'flipnzee-search'
	);

	$table->display();

	?>

</form>

Using a GET form allows WordPress to send the search term as part of the page URL.


Step 3 — Display Success Messages

Before displaying the table, retrieve the message parameter:

$message = isset( $_GET['message'] )
	? sanitize_text_field(
		wp_unslash( $_GET['message'] )
	)
	: '';

Display the success notice:

<?php if ( 'deleted' === $message ) : ?>

<div class="notice notice-success is-dismissible">
	<p>Auction deleted successfully.</p>
</div>

<?php endif; ?>

This keeps the interface consistent with earlier lessons where auctions could be added, edited, or deleted.


Step 4 — Keep the Search Box Inside the Method

One mistake encountered during implementation was accidentally placing:

$message = ...

outside the all_auctions_page() method.

Doing so resulted in the following PHP parse error:

PHP Parse error:
syntax error,
unexpected variable "$message",
expecting "function" or "const"

The fix was simply moving the code back inside the method.


Step 5 — Validate the PHP File

Before uploading the updated plugin, validate the modified file.

From the project root:

php -l admin/class-admin.php

or from inside the admin folder:

php -l class-admin.php

Expected output:

No syntax errors detected in class-admin.php

Step 6 — Test the Plugin

After uploading the updated plugin:

  1. Activate the plugin.
  2. Navigate to:

Flipnzee Auctions → All Auctions

You should now see:

  • A search box above the auctions table.
  • Pagination continuing to work.
  • Existing auctions displaying normally.

At this stage, the search box is visible but not yet filtering results. That functionality will be added in the next implementation.


Troubleshooting

During this implementation, the following issue occurred:

Problem

The plugin could not be activated because of a PHP syntax error.

Cause

The $message variable and notice code had been placed outside the all_auctions_page() method.

Solution

Move the code back inside the method and validate the file using:

php -l

before uploading the plugin again.


Result

At the end of this implementation:

  • ✅ Search box added
  • ✅ Pagination still functional
  • ✅ Plugin activates correctly
  • ✅ Admin interface looks more professional
  • ✅ Foundation prepared for database search in the next implementation

Source Code

At the beginning of this article, you can download the plugin source code before implementing Lesson 26.

At the end of this article, you can download the updated source code after completing Lesson 26 and compare the changes with your own implementation.

⬇ Download Plugin (After Lesson 26) (0 downloads )

Next: In the next implementation article, we’ll connect the search box to the database query so administrators can search auctions by ID, Listing ID, or Status while keeping pagination fully functional.

Lesson 25 Implementation Guide — Adding Pagination to the Auctions Table


Prerequisites

Before starting this implementation:

  • Lesson 25 has been completed.
  • The Auctions table is already displaying data using WP_List_Table.
  • Auctions can already be created, edited and deleted.

In this guide, we’ll implement pagination so the table can efficiently display a large number of auction records.


Step 1 — Modify get_all_auctions()

Open:

includes/class-auction-manager.php

Locate the existing method:

public static function get_all_auctions()

Replace it with a version that accepts two parameters:

  • $per_page
  • $offset

The SQL query should use:

LIMIT

and

OFFSET

so only the required records are retrieved from the database.

Why?

Without pagination, WordPress loads every auction record into memory.

With pagination enabled, only the current page of results is loaded.

This becomes much faster as the number of auctions grows.


Step 2 — Add a Method to Count Auctions

Still inside:

includes/class-auction-manager.php

Create a new method:

count_auctions()

This method simply returns:

SELECT COUNT(*)

from the auctions table.

Why?

The table needs to know:

  • total number of auctions
  • number of pages

before WordPress can generate pagination links.


Step 3 — Update prepare_items()

Open:

admin/class-auctions-table.php

Locate:

prepare_items()

Instead of loading every auction:

Flipnzee_Auction_Manager::get_all_auctions();

calculate:

  • current page
  • records per page
  • SQL offset

Then call:

Flipnzee_Auction_Manager::get_all_auctions(
    $per_page,
    $offset
);

Also configure:

set_pagination_args()

using the total auction count.


Step 4 — Upload the Updated Plugin

Compress the updated plugin.

Install it on the test website.

Visit:

Flipnzee Auctions
→ All Auctions

If enough auction records exist, pagination links should now appear beneath the table.


Step 5 — Test Pagination

Create multiple auction records.

Verify that:

  • Page 1 loads correctly.
  • Page 2 displays the next set of auctions.
  • Previous and Next links work.
  • Auctions remain sorted correctly.
  • Edit and Delete continue to work on every page.

Result

After completing this implementation, the Auctions table behaves much more like a professional WordPress administration screen.

Instead of loading every record at once, the plugin displays a manageable number of auctions per page, improving performance and scalability as the number of auctions increases.


What We Learned

In this implementation, we learned how to:

  • Modify database queries using LIMIT and OFFSET.
  • Count database records efficiently.
  • Configure pagination using WP_List_Table.
  • Calculate SQL offsets based on the current page.
  • Display paginated auction records in the WordPress admin area.

Download Source Code

⬇ Download Plugin (After Lesson 25) (0 downloads )

Next Implementation Guide: Lesson 26 — Adding Auction Search Functionality to the Admin Table