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.
  • Completed Project: Download the updated plugin after implementing the functional search feature and compare your changes if needed.

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 27 — Implementing Functional Search in a Custom WP_List_Table

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


Introduction

In Lesson 26, we successfully added a professional search box above the auctions table. Although the interface looked complete, typing a search term did not actually filter the records.

In this lesson, we’ll connect the search box to the database so administrators can search auctions directly from the WordPress dashboard. This brings our custom WP_List_Table much closer to the functionality of WordPress’ built-in administration screens.


What You’ll Learn

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

  • Read search keywords submitted by WP_List_Table
  • Pass search terms to your database query
  • Filter auction records using SQL
  • Keep pagination working while searching
  • Display only matching auctions

Why Search Matters

Imagine managing hundreds or even thousands of auctions.

Without search, finding Auction ID 357 or Listing ID 8421 would require scrolling through multiple pages.

A search box allows administrators to instantly locate records, improving productivity and making the plugin feel much more professional.


How WordPress Search Works

When you click the Search button, WordPress automatically sends the search keyword as a GET parameter named:

s

For example:

admin.php?page=flipnzee-all-auctions&s=44

Your plugin simply needs to:

  1. Read $_REQUEST['s']
  2. Pass it to the database
  3. Display matching records

Step 1 — Read the Search Term

Inside the table class, retrieve the search keyword submitted by WordPress.

Example:

$search = isset( $_REQUEST['s'] )
	? sanitize_text_field(
		wp_unslash( $_REQUEST['s'] )
	)
	: '';

Always sanitize user input before using it.


Step 2 — Pass the Search Term

Modify the call to retrieve auctions.

Instead of:

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

pass the search term as well:

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

Step 3 — Update the SQL Query

Inside the Auction Manager, check whether a search keyword exists.

If it does, filter the results.

Example SQL:

WHERE
	id LIKE ?
	OR listing_id LIKE ?
	OR status LIKE ?

This allows administrators to search by:

  • Auction ID
  • Listing ID
  • Auction Status

Step 4 — Keep Pagination Working

Searching should not disable pagination.

Instead of counting every auction:

SELECT COUNT(*)

count only the matching records.

This keeps the page numbers accurate.


Step 5 — Test the Feature

Create several auctions.

Then try searches like:

44
draft
21

The table should immediately display only the matching auctions.


Security Considerations

Never place raw user input directly into SQL queries.

Always use:

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

These functions protect your plugin against SQL injection attacks.


Common Mistakes

Many developers accidentally:

  • Forget to sanitize the search term
  • Build SQL using string concatenation
  • Break pagination after filtering
  • Ignore empty search values

Avoiding these mistakes results in a more secure and reliable plugin.


Expected Result

After completing this lesson:

  • ✅ Search box filters auctions
  • ✅ Pagination still works
  • ✅ Database queries remain secure
  • ✅ Administrators can quickly find auction records
  • ✅ The Auctions page behaves much more like WordPress core admin tables

Conclusion

Adding a search box was only the first step. Connecting it to the database transforms it into a practical tool for managing real auction data.

With searchable, paginated listings now in place, the Flipnzee Auctions plugin continues to evolve from a learning project into a production-ready WordPress plugin.


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

In the Next Lesson

We’ll further enhance the Auctions table by allowing administrators to sort auctions by clicking the table headers, such as:

  • Auction ID
  • Listing ID
  • Start Price
  • Status
  • Created Date

This is another feature users expect from professional WordPress plugins and will make navigating large auction datasets even easier.

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.


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 26 — Adding Search Functionality to the Auctions Table Using WP_List_Table

As the number of auctions grows, scrolling through multiple pages becomes inefficient. WordPress solves this problem by providing a built-in search box in WP_List_Table. In this lesson, we’ll integrate that search feature into the Flipnzee Auctions plugin so administrators can quickly locate auctions by ID, Listing ID, or Status.

By the end of this lesson, the Auctions screen will feel much closer to native WordPress admin pages such as Posts, Pages, and Users.


What You’ll Build

After completing this lesson, administrators will be able to:

  • Search auctions from the admin table.
  • Search by Auction ID.
  • Search by Listing ID.
  • Search by Status.
  • Combine search with pagination.
  • Display only matching auction records.

Why Add Search?

Imagine managing hundreds or even thousands of auctions.

Without search, finding Auction #437 would require browsing through many pages.

A search box allows administrators to locate records instantly, improving productivity and making the plugin feel much more professional.


How WordPress Search Works

WP_List_Table already provides a search box.

However, it only displays the search field.

It is the developer’s responsibility to:

  • Display the search box.
  • Read the search keyword.
  • Modify the database query.
  • Return only matching results.

Step 1 — Add a Search Box Above the Table

Update the All Auctions admin page.

Before displaying the table, output the search form using the built-in method:

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

This automatically creates a familiar WordPress search field.


Step 2 — Read the Search Keyword

Retrieve the search term safely using:

  • sanitize_text_field()
  • wp_unslash()

If no search keyword is provided, continue displaying all auctions.


Step 3 — Update the Auction Manager

Modify the query that retrieves auctions.

If a search term exists, search multiple columns such as:

  • Auction ID
  • Listing ID
  • Status

using SQL conditions.


Step 4 — Count Matching Results

Pagination should work with search.

Instead of counting every auction, count only the matching auctions.

This ensures the page numbers remain accurate.


Step 5 — Preserve Search During Pagination

When moving from Page 1 to Page 2, the search keyword should remain in the URL.

Otherwise, WordPress would forget the search and display every auction again.


Step 6 — Test the Feature

Create several auctions with different:

  • Listing IDs
  • Prices
  • Status values

Then verify:

  • Searching by Listing ID returns the correct auction.
  • Searching by Status works.
  • Pagination still functions correctly.
  • No PHP warnings appear.

Example Workflow

Suppose your table contains:

Auction IDListing IDStatus
1101Draft
2215Active
3330Closed
4450Active

Searching for:

215

returns only Auction #2.

Searching for:

Active

returns Auctions #2 and #4.


Tips

  • Always sanitize search input before using it.
  • Use $wpdb->prepare() when building SQL queries.
  • Ensure pagination and search work together.
  • Test searches that return zero results.
  • Keep the search lightweight for better performance.

What We Learned

In this lesson, you learned how to:

  • Add a search box to a WP_List_Table.
  • Capture user search input securely.
  • Search auction records using SQL.
  • Combine search with pagination.
  • Build a more professional WordPress administration interface.

Next Lesson

In Lesson 27, we’ll make the Auctions table even more user-friendly by adding sortable columns, allowing administrators to sort auctions by:

  • Auction ID
  • Listing ID
  • Start Price
  • Reserve Price
  • Buy Now Price
  • Status
  • Creation Date

This will make managing large numbers of auctions much faster and more intuitive.

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


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

Lesson 25 — Adding Pagination to the Auctions Table Using WP_List_Table

By Lesson 24, our Flipnzee Auctions plugin already supports:

  • Creating auctions
  • Viewing all auctions
  • Editing auctions
  • Deleting auctions
  • Displaying professional success/error notices

However, there is still one major limitation.

If your marketplace eventually contains hundreds or even thousands of auctions, displaying every auction on a single page becomes slow and difficult to use.

WordPress solves this problem with pagination, and since we’re already using WP_List_Table, adding it is surprisingly straightforward.

In this lesson, we’ll make our Auctions page production-ready by adding pagination.


Why Pagination Matters

Imagine your marketplace has:

  • 500 active auctions
  • 2,000 completed auctions
  • 15,000 historical auctions

Loading every record at once would:

  • increase page load time
  • consume unnecessary memory
  • make scrolling frustrating
  • reduce the overall admin experience

Instead, WordPress displays only a limited number of records per page.

Example:

Showing 20 auctions per page

Page 1
Page 2
Page 3
...
Page 150

This is how professional WordPress plugins behave.


How WP_List_Table Supports Pagination

The class already provides pagination support.

We only need to tell WordPress:

  • how many total items exist
  • how many items to display per page
  • which page the user is currently viewing

WordPress automatically creates:

  • Previous button
  • Next button
  • Page numbers
  • Current page indicator

Current Problem

Our current prepare_items() method probably looks similar to this:

$this->items = Flipnzee_Auction_Manager::get_all_auctions();

This loads every auction.

Instead, we should only load the current page.


Step 1 — Count Total Auctions

First, determine how many auctions exist.

Example:

$total_items =
	Flipnzee_Auction_Manager::count_auctions();

Step 2 — Decide Items Per Page

Choose how many auctions should appear on each page.

Example:

$per_page = 20;

Twenty items per page is a common default in WordPress.


Step 3 — Determine the Current Page

WordPress passes the current page automatically.

Example:

$current_page = $this->get_pagenum();

If the user clicks Page 4,

$current_page = 4

Step 4 — Calculate Database Offset

SQL needs to know where to begin.

Formula:

Offset =
(Current Page − 1)
×
Items Per Page

Example:

Page 1

Offset = 0

Page 2

Offset = 20

Page 3

Offset = 40

Only the required rows are retrieved.


Step 5 — Retrieve Only the Required Auctions

Instead of fetching everything:

SELECT *
FROM auctions

retrieve only the current page.

Example SQL:

SELECT *
FROM auctions
LIMIT 20 OFFSET 40

This loads only the records needed for the current page.


Step 6 — Configure Pagination

Finally, tell WP_List_Table how many pages exist.

Example:

$this->set_pagination_args(
	array(
		'total_items' => $total_items,
		'per_page'    => $per_page,
	)
);

WordPress calculates the number of pages automatically.


The Result

Instead of one endlessly long table:

Auction 1
Auction 2
Auction 3
...
Auction 2000

your users will see:

Auction 1
Auction 2
...
Auction 20

← Previous

1 2 3 4 5

Next →

This provides a cleaner and faster administration experience.


Why This Matters

Pagination is not just about convenience.

It also:

  • Improves performance
  • Reduces memory usage
  • Speeds up database queries
  • Makes large datasets manageable
  • Follows WordPress admin standards
  • Prepares your plugin for production use

What You’ll Learn Next

In Lesson 26, we’ll add sortable columns to the Auctions table so administrators can sort auctions by:

  • Auction ID
  • Listing ID
  • Start Price
  • Buy Now Price
  • Status
  • Creation Date

Sorting and pagination together make your auction management screen feel like a professional WordPress plugin rather than a basic prototype.

Lesson 25 Implementation Guide — Adding Pagination to the Auctions Table

Lesson 24 – Displaying Professional Admin Success and Error Notices in Your WordPress Plugin

In the previous lesson, we successfully implemented the ability to delete auctions from the Flipnzee Auctions plugin. The feature works correctly, but from a user’s perspective there is one thing missing—feedback.

Imagine clicking Delete and returning to the auction list with no indication whether the deletion actually succeeded. Users may wonder whether the auction was removed or if something went wrong.

In this lesson, we’ll improve the user experience by displaying professional WordPress admin notices after important actions such as creating, updating, and deleting auctions.


Why Admin Notices Matter

WordPress itself uses admin notices throughout the dashboard.

For example:

  • Post published.
  • Settings saved.
  • Plugin activated.
  • Theme installed.

Users have become accustomed to seeing these messages. Your plugin should follow the same design pattern.


What We’ll Build

By the end of this lesson, the plugin will display messages such as:

  • Auction created successfully.
  • Auction updated successfully.
  • Auction deleted successfully.
  • Unable to delete auction.
  • Unable to update auction.

Each message will use the standard WordPress notice styles.


Passing Messages Between Pages

After an action is completed, the user is redirected back to an admin page.

For example:

wp_safe_redirect(
	admin_url(
		'admin.php?page=flipnzee-all-auctions&message=deleted'
	)
);

Notice the query parameter:

message=deleted

This allows the next page to know what happened.


Reading the Message

Inside the destination page we retrieve the message safely.

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

This ensures that only sanitized input is used.


Displaying a Success Notice

If the message equals deleted, we display a success notice.

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

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

<?php endif; ?>

The classes are provided by WordPress.

  • notice
  • notice-success
  • is-dismissible

No custom CSS is required.


Displaying an Error Notice

If something goes wrong, we display an error.

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

<div class="notice notice-error is-dismissible">
	<p>Unable to delete auction.</p>
</div>

<?php endif; ?>

WordPress automatically styles it using the familiar red error notice.


Why Redirect Instead of Printing Messages?

Suppose we deleted the record and immediately echoed:

Auction deleted.

The user would remain on the processing page.

Instead, WordPress plugins usually:

  1. Perform the action.
  2. Redirect.
  3. Display a message.

This pattern avoids duplicate submissions when the browser is refreshed.


Benefits of This Approach

Our plugin now provides:

  • Clear feedback to administrators.
  • Consistent WordPress user experience.
  • Cleaner navigation.
  • Better security through redirects.
  • Professional appearance.

Best Practices Learned

Throughout this lesson we reinforced several WordPress development practices:

  • Use wp_safe_redirect() after processing forms.
  • Pass status information using query parameters.
  • Sanitize all incoming data.
  • Use WordPress admin notice classes.
  • Keep users informed after every important action.

What We’ve Achieved So Far

At this point, the Flipnzee Auctions plugin supports:

  • Plugin activation
  • Database creation
  • Admin dashboard
  • Add Auction
  • View Auctions
  • Edit Auction
  • Delete Auction
  • Admin notices
  • Secure nonces
  • CRUD operations

The plugin is beginning to feel like a real production-ready WordPress application rather than a simple demonstration project.


Coming Up Next

In Lesson 25, we’ll make the auction management screen even more powerful by adding Bulk Actions, allowing administrators to delete multiple auctions at once, just like the built-in WordPress Posts and Pages screens.

Lesson 23: Adding Secure Auction Deletion to Your WordPress Plugin


Deleting data is one of the most sensitive operations in any application. A single mistake can accidentally remove valuable records or create a serious security vulnerability.

In this lesson, we’ll build a secure Delete Auction feature for the Flipnzee Auctions plugin using WordPress best practices.

Why a Delete Feature Matters

As administrators manage auctions over time, some records become unnecessary:

  • Test auctions
  • Duplicate auctions
  • Expired drafts
  • Incorrect listings

Instead of manually deleting records from phpMyAdmin, administrators should be able to remove auctions directly from the WordPress dashboard.


Step 1 — Add a Delete Link

Inside our custom WP_List_Table, we added a new row action.

'delete' => sprintf(
    '<a href="%s" onclick="return confirm(\'Are you sure you want to delete this auction?\');">Delete</a>',
    wp_nonce_url(
        admin_url(
            'admin-post.php?action=flipnzee_delete_auction&auction_id=' . absint( $item->id )
        ),
        'flipnzee_delete_auction'
    )
),

This generates a secure URL for every auction.


Step 2 — Protect the Request with a Nonce

Deleting records should never rely only on an auction ID.

Instead, WordPress adds a nonce to the URL.

A nonce helps verify that:

  • the request originated from your website
  • the current administrator intentionally clicked Delete
  • attackers cannot easily forge deletion requests

Step 3 — Display a Confirmation Dialog

Before the browser follows the Delete link, JavaScript displays:

Are you sure you want to delete this auction?

This gives administrators one final chance to cancel.

It is a simple but important safeguard.


Step 4 — Register the Delete Action

WordPress routes admin form submissions and custom actions through the admin_post hook.

We registered:

add_action(
    'admin_post_flipnzee_delete_auction',
    array( $this, 'handle_delete_auction' )
);

Now WordPress knows exactly which method should process the deletion request.


Step 5 — Verify the Nonce

Inside our handler we verify the request.

check_admin_referer(
    'flipnzee_delete_auction'
);

If the nonce is invalid, WordPress immediately stops execution.

This protects the plugin from Cross-Site Request Forgery (CSRF) attacks.


Step 6 — Delete the Database Record

The Auction Manager performs the actual deletion.

Flipnzee_Auction_Manager::delete_auction(
    $auction_id
);

Keeping database operations inside the manager class keeps the code organized and easier to maintain.


Step 7 — Redirect Back

After deletion, the administrator is redirected back to the auction list.

wp_safe_redirect(
    admin_url(
        'admin.php?page=flipnzee-all-auctions'
    )
);

This prevents accidental duplicate requests if the page is refreshed.


What We Learned

In this lesson we learned how to:

  • Add custom row actions to WP_List_Table
  • Generate secure admin URLs
  • Protect delete operations using WordPress nonces
  • Display JavaScript confirmation dialogs
  • Handle custom admin_post actions
  • Remove database records safely
  • Redirect users after completing an action

Why This Matters

Delete functionality may seem simple, but implementing it securely is an important milestone in WordPress plugin development.

By following WordPress coding standards—using nonces, confirmation dialogs, dedicated manager classes, and proper redirects—you create a plugin that is both user-friendly and resistant to common security risks.

In the next lesson, we’ll continue enhancing the Flipnzee Auctions plugin by adding more professional management features to make auction administration even more powerful.

Lesson 22: Deleting Auctions Securely with Confirmation in WordPress

In the previous lessons, we successfully built the ability to create, view, and edit auctions from the WordPress admin panel. The final piece of the basic CRUD (Create, Read, Update, Delete) functionality is allowing administrators to safely delete auctions.

Deleting records is a destructive operation, so it must be implemented carefully. A poorly designed delete feature could allow accidental deletions or even expose your plugin to security vulnerabilities. In this lesson, we’ll build a secure delete system using WordPress best practices.


Why Deleting Requires Special Attention

Unlike creating or editing records, deleting permanently removes data from the database. This means we should always:

  • Verify the user’s permissions.
  • Protect against CSRF attacks using WordPress nonces.
  • Ask the administrator for confirmation.
  • Delete only the intended auction.
  • Redirect back with a success or error message.

Fortunately, WordPress provides built-in tools that make implementing secure deletion straightforward.


What We’ll Build

By the end of this lesson, every auction listed in the All Auctions page will include a Delete link.

The workflow will look like this:

  1. Administrator clicks Delete.
  2. A confirmation dialog appears.
  3. Clicking Cancel stops the process.
  4. Clicking OK sends a secure request.
  5. The selected auction is removed from the database.
  6. The administrator is redirected back to the auction list with a success message.

Step 1 — Create a Delete Method in the Auction Manager

Inside:

includes/class-auction-manager.php

we’ll add a new method named:

delete_auction( $auction_id )

This method will use WordPress’s $wpdb->delete() function to remove a single auction based on its ID.

Keeping database operations inside the Auction Manager keeps our plugin organized and follows the same architecture we’ve used for creating and updating auctions.


Step 2 — Handle Delete Requests

Next, we’ll open:

admin/class-admin-posts.php

and register another admin action.

Instead of processing form submissions, this action will process delete requests coming from the auction list.

The handler will:

  • verify the nonce
  • validate the auction ID
  • call delete_auction()
  • redirect back to the auction list

Separating request handling from database logic keeps the code easier to maintain.


Step 3 — Add Delete Links to the Auction Table

Our WP_List_Table currently displays an Edit action for every auction.

We’ll modify the Actions column so that every row displays:

Edit | Delete

The Delete link will include:

  • auction ID
  • WordPress nonce
  • delete action

This allows WordPress to verify that the request genuinely originated from an authorized administrator.


Step 4 — Display a Confirmation Dialog

Even administrators sometimes click the wrong link.

To prevent accidental deletions, we’ll attach a simple JavaScript confirmation dialog.

When the administrator clicks Delete, WordPress will ask:

Are you sure you want to delete this auction?

Selecting Cancel aborts the request.

Selecting OK continues with the deletion.

This small addition greatly improves the user experience while reducing accidental mistakes.


Why WordPress Uses Nonces for Delete Operations

Imagine an administrator is logged into WordPress and unknowingly visits a malicious website.

Without nonce protection, that website could secretly trigger requests that delete auctions from your plugin.

A WordPress nonce ensures that delete requests originate from your own plugin and are intentionally initiated by the administrator.

Although nonces are not passwords or encryption keys, they provide an important layer of protection against Cross-Site Request Forgery (CSRF) attacks.


Expected Result

Once this lesson is complete, the All Auctions page will look similar to this:

Auction IDListingStatusActions
1222DraftEdit | Delete
255ActiveEdit | Delete
3108ClosedEdit | Delete

Clicking Delete will display a confirmation dialog before permanently removing the auction.


What You’ll Learn

By completing this lesson, you’ll understand:

  • How to delete database records using $wpdb->delete()
  • How WordPress processes admin actions
  • Why delete operations require nonces
  • How to generate secure action links
  • How to redirect after completing an operation
  • How to improve usability with confirmation dialogs

Coming Up Next

In Lesson 23, we’ll make the auction management screen much more powerful by adding search, sorting, filtering, and pagination to our custom WP_List_Table. These features become essential as the number of auctions grows, helping administrators quickly locate and manage specific records.


Conclusion

With the addition of secure deletion, our auction plugin will support the complete set of CRUD operations—Create, Read, Update, and Delete. More importantly, we’ll implement this functionality using WordPress coding standards and security best practices, laying the foundation for a robust and production-ready auction management system.