Implementation Lesson 35: Manually Activate Scheduled Auctions in the Flipnzee Auctions Plugin

In the previous lesson, we added Auction Start and Auction End scheduling fields to our auctions. However, simply storing these dates in the database is not enough. The plugin also needs a mechanism to activate auctions when their scheduled start time arrives.

In this implementation lesson, we built the first version of that mechanism by adding a Manual Auction Activation feature.

Although the final version will eventually run automatically using WordPress Cron, implementing a manual activation tool first makes development, testing, and debugging much easier.


What We Built

By the end of this lesson, our plugin can:

  • Store scheduled auction start dates.
  • Compare scheduled start dates with the current WordPress time.
  • Activate eligible auctions.
  • Provide an administrator button to run the activation process manually.
  • Lay the foundation for future automation.

Step 1: Create the Activation Function

Inside class-auction-manager.php, we created a new method:

public static function activate_scheduled_auctions()

This method:

  • Retrieves the current WordPress time.
  • Searches for auctions whose:
    • status is draft
    • Auction Start has passed
  • Updates their status to:
active

Step 2: Use a Single SQL UPDATE Query

Instead of loading every auction into PHP, we used one efficient SQL query.

The query updates every matching auction in one operation.

Benefits include:

  • Better performance
  • Cleaner code
  • Easier maintenance
  • Scales well for hundreds or thousands of auctions

Step 3: Add a Dashboard Button

Inside the Flipnzee Auctions dashboard, we added a new button:

Activate Scheduled Auctions

Clicking this button executes the activation process immediately.

This allows us to verify our scheduling logic before introducing automatic background processing.


Step 4: Register a Secure Admin Action

We registered a custom admin action using:

admin_post_flipnzee_activate_scheduled_auctions

The handler performs several important tasks:

  • Capability check
  • Nonce verification
  • Calls the activation function
  • Redirects back to the dashboard

This follows standard WordPress security practices.


Step 5: Add Nonce Protection

Every activation request includes a WordPress nonce.

Before processing, the plugin verifies that the request originated from the WordPress administration area.

This prevents Cross-Site Request Forgery (CSRF) attacks.


Step 6: Test the Feature

Testing involved:

  • Creating multiple auctions
  • Assigning different Auction Start dates
  • Saving them
  • Clicking Activate Scheduled Auctions
  • Verifying database values using phpMyAdmin

This confirmed that our activation logic was executing correctly.


Debugging an Unexpected Issue

During testing, auctions were not changing from Draft to Active, even though their scheduled start times had already passed.

Instead of assuming the SQL query was incorrect, we debugged the process step by step.


Checking the Database

Using phpMyAdmin, we verified that:

  • Auction Start values were stored correctly.
  • Auction End values were stored correctly.
  • Auction status remained draft.

This confirmed that the data itself was not the problem.


Verifying the SQL Logic

Next, we reviewed the SQL UPDATE statement.

The query correctly selected auctions where:

  • status = draft
  • auction_start <= current WordPress time

No issues were found in the SQL itself.


Inspecting the Current WordPress Time

To isolate the issue, we temporarily added:

wp_die( current_time( 'mysql' ) );

This allowed us to display the exact time that WordPress was using during the activation process.

The output revealed that WordPress was using a different timezone than expected.


Root Cause

The activation logic depended on:

current_time( 'mysql' )

while the stored auction schedule had been entered using local time.

As a result:

  • Auction Start appeared to be in the future.
  • The SQL condition never matched.
  • No auctions were activated.

The activation function itself was working correctly.


Lessons Learned

This debugging session reinforced several important development principles.

Instead of immediately changing the SQL query, we:

  • Verified the stored database values.
  • Confirmed the SQL logic.
  • Checked the current application time.
  • Identified the real source of the problem.

This systematic approach saved considerable time and prevented unnecessary code changes.


Why We Built Manual Activation First

Eventually, auctions should activate automatically.

However, during development, a manual activation tool provides several advantages:

  • Easier debugging
  • Immediate testing
  • No dependency on WP-Cron
  • Faster development cycle

Once the activation logic has been thoroughly tested, replacing the manual button with automatic scheduling becomes straightforward.


What’s Next?

In the next lesson, we’ll complete the scheduling workflow by implementing Manual Auction Closing.

Auctions whose scheduled end time has passed will automatically transition from Active to Closed, laying another important foundation for a production-ready auction platform.


Download Source Code

Download the starting version before this lesson:

โฌ‡ Download Plugin (After Lesson 34) (0 downloads )

Download the completed version after implementing this lesson:

โฌ‡ Download Plugin (After Lesson 35) (0 downloads )

Implementation Lesson 34: Display Auction Start and End Dates in the Auctions Table

In the previous lesson, we added Auction Start and Auction End fields to the Add Auction and Edit Auction forms. However, administrators still could not see these values from the All Auctions page.

In this implementation lesson, we’ll enhance the auction management table by displaying both dates in a clean, human-readable format. We’ll also ensure that missing or invalid dates are handled gracefully.


What We’ll Build

By the end of this lesson, the All Auctions table will display:

  • Auction Start
  • Auction End
  • Properly formatted dates
  • A dash (โ€”) whenever no date has been configured

Instead of displaying raw database values such as:

2026-07-02 14:06:00

the table will display:

02 Jul 2026 14:06

which is much easier for administrators to read.


Step 1: Add the New Columns

Open:

admin/class-auctions-table.php

Locate the get_columns() method.

Add two new columns:

'auction_start' => 'Auction Start',
'auction_end'   => 'Auction End',

Your auctions table will now include two additional headings.


Step 2: Format the Dates

Locate the column_default() method.

Instead of returning the raw database value, we’ll format the dates using WordPress’ wp_date() function.

For the Auction Start column:

case 'auction_start':

    $timestamp = strtotime( $item->auction_start );

    if (
        empty( $item->auction_start ) ||
        '0000-00-00 00:00:00' === $item->auction_start ||
        false === $timestamp
    ) {
        return 'โ€”';
    }

    return wp_date(
        'd M Y H:i',
        $timestamp
    );

Repeat the same logic for the Auction End column:

case 'auction_end':

    $timestamp = strtotime( $item->auction_end );

    if (
        empty( $item->auction_end ) ||
        '0000-00-00 00:00:00' === $item->auction_end ||
        false === $timestamp
    ) {
        return 'โ€”';
    }

    return wp_date(
        'd M Y H:i',
        $timestamp
    );

Why Use wp_date()?

Although PHP provides the date() function, WordPress recommends using wp_date() because it:

  • Respects the site’s configured timezone
  • Produces consistent output across WordPress
  • Follows WordPress coding standards
  • Makes plugins more portable

Step 3: Handle Missing Dates Gracefully

Many auctions may not yet have a configured start or end time.

Instead of showing confusing values like:

0000-00-00 00:00:00

or

30 Nov -0001 00:00

our implementation simply displays:

โ€”

This creates a much cleaner interface for administrators.


Step 4: Make the Columns Sortable

To improve usability, add the new columns to the sortable columns list.

Inside get_sortable_columns():

'auction_start' => array( 'auction_start', false ),
'auction_end'   => array( 'auction_end', false ),

Then update the list of allowed database columns inside:

includes/class-auction-manager.php

Add:

'auction_start',
'auction_end',

to the $allowed_columns array.

This allows administrators to sort auctions by either start or end date.


Step 5: Test the Implementation

Create a few auctions with different schedules.

Verify that:

  • Auction Start displays correctly.
  • Auction End displays correctly.
  • Empty dates display as โ€”.
  • Clicking the column headings sorts the table.
  • Existing auctions without dates continue to work normally.

Final Result

The All Auctions page now provides administrators with immediate visibility into the auction schedule without opening each auction individually.

The table is easier to scan, easier to sort, and provides a much more professional management experience.


Troubleshooting Tips

If you encounter unexpected date values:

  • Verify that the database contains valid DATETIME values.
  • Use wp_date() instead of date().
  • Check that strtotime() returns a valid timestamp.
  • Display โ€” whenever the date is empty, invalid, or equal to 0000-00-00 00:00:00.

A quick syntax check can also help before packaging the plugin:

php -l admin/class-auctions-table.php

If the command reports:

No syntax errors detected

your PHP syntax is valid.


What We Learned

In this lesson, we learned how to:

  • Extend a custom WP_List_Table
  • Add new columns to an admin table
  • Format database dates for display
  • Use wp_date() following WordPress best practices
  • Handle empty and invalid dates gracefully
  • Make custom columns sortable

These improvements make the Flipnzee Auctions plugin feel much closer to a polished, production-ready WordPress plugin while laying the groundwork for future features such as automatic auction activation, countdown timers, and bid management.

Download Source Code

Download the starting version of the plugin before the lesson:

โฌ‡ Download Plugin (After Lesson 33) (0 downloads )

Download the completed version after this lesson:

โฌ‡ Download Plugin (After Lesson 34) (0 downloads )

Lesson 33 Implementation: Adding Auction Scheduling (Start & End Date/Time)

In Lesson 33, we transformed the Flipnzee Auctions plugin from a basic auction management system into one capable of supporting scheduled auctions.

Instead of auctions existing only in a draft, active, or closed state, administrators can now define exact start and end dates. This lays the foundation for future automation, where auctions will open and close automatically without manual intervention.


What We Implemented

During this lesson we added support for:

  • Auction Start Date & Time
  • Auction End Date & Time
  • Saving schedule information while creating auctions
  • Editing scheduled auctions
  • Updating the schedule after creation
  • Database support for scheduling

This is a major step toward building a production-ready auction platform.


Step 1 โ€“ Extend the Database

The first task was updating the auction table to store scheduling information.

Inside includes/class-database.php we added two new columns:

auction_start DATETIME NULL,
auction_end DATETIME NULL,

These fields allow every auction to have:

  • Start date
  • End date

Both fields are optional, making the feature flexible.


Step 2 โ€“ Update the Add Auction Form

Next we modified the Add Auction screen.

Two new fields were added:

  • Auction Start
  • Auction End

Both use HTML5’s built-in datetime picker.

<input type="datetime-local">

Benefits:

  • Native browser calendar
  • Native time selector
  • No JavaScript library required

Step 3 โ€“ Save the Schedule

Adding form fields is not enough.

We also updated:

admin/class-admin-posts.php

to read:

$_POST['auction_start']
$_POST['auction_end']

The values are sanitized using:

sanitize_text_field()

before being sent to the Auction Manager.


Step 4 โ€“ Update create_auction()

The next task was modifying:

Flipnzee_Auction_Manager::create_auction()

Its function signature changed from:

create_auction(
    $listing_id,
    $start_price,
    $reserve_price,
    $buy_now_price
)

to:

create_auction(
    $listing_id,
    $start_price,
    $reserve_price,
    $buy_now_price,
    $auction_start,
    $auction_end
)

The SQL INSERT statement now stores:

'auction_start'
'auction_end'

alongside the other auction fields.


Step 5 โ€“ Update the Edit Auction Screen

Once scheduling could be saved, administrators also needed the ability to edit it.

Inside:

admin/class-admin.php

we added two additional form fields:

  • Auction Start
  • Auction End

The stored values are displayed using:

str_replace(
    ' ',
    'T',
    $auction->auction_start
)

This converts the MySQL format:

2026-07-02 14:30:00

into the format expected by HTML5:

2026-07-02T14:30

Without this conversion, the datetime picker would appear empty.


Step 6 โ€“ Update update_auction()

The update method also required changes.

Its function signature was expanded to accept:

$auction_start
$auction_end

The SQL UPDATE statement now saves:

'auction_start'
'auction_end'

along with:

  • Listing ID
  • Prices
  • Status

This allows administrators to modify auction schedules after creation.


Step 7 โ€“ Verify Everything

After completing the implementation we tested:

Creating Auctions

โœ” Auction created successfully.

Editing Auctions

โœ” Auction Start displayed correctly.

โœ” Auction End displayed correctly.

Saving Changes

โœ” Updated schedule persisted successfully.

No PHP syntax errors were reported during testing.


What We Learned

This lesson introduced several useful concepts:

  • Extending existing database tables
  • Working with HTML5 datetime inputs
  • Handling date/time values safely
  • Updating SQL INSERT statements
  • Updating SQL UPDATE statements
  • Passing additional parameters between classes
  • Displaying MySQL datetime values inside HTML forms

Current Progress

The plugin now supports:

  • Creating auctions
  • Editing auctions
  • Searching auctions
  • Sorting auctions
  • Status filtering
  • Bulk deletion
  • Auction scheduling

The scheduling functionality is now fully functional from an administrator’s perspective.

Download Source Code

Download the starting version of the plugin before the lesson:

โฌ‡ Download Plugin (After Lesson 32) (6 downloads )

Download the completed version after this lesson:

โฌ‡ Download Plugin (After Lesson 33) (0 downloads )

What’s Next?

In the next lesson, we’ll complete the scheduling feature by displaying Auction Start and Auction End directly in the All Auctions table. This will allow administrators to view an auction’s schedule at a glance without opening the edit screen.

After that, we’ll be ready to begin implementing automatic auction opening and closing, bringing the plugin one step closer to a production-ready auction platform.

Lesson 32 Implementation โ€“ Adding a Fully Functional Status Filter to the Auctions Table

In the previous lesson, we added the Status dropdown above the auctions table. Although the user interface looked complete, the filter was only partially functional. Selecting a status displayed filtered results, but the search box and pagination count did not always stay synchronized.

In this lesson, we’ll complete the implementation by making the Status Filter, Search Box, and Pagination work together seamlessly.


Prerequisites

Before starting, ensure you have completed:

  • Lesson 29 โ€“ Bulk Actions
  • Lesson 30 โ€“ Bulk Delete
  • Lesson 31 โ€“ Adding a Status Filter Dropdown

Step 1 โ€“ Read the Selected Status

Open:

admin/class-auctions-table.php

Inside the prepare_items() method, read the selected status.

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

Using sanitize_text_field() and wp_unslash() follows WordPress coding standards and safely processes user input.


Step 2 โ€“ Pass the Status to the Count Query

Previously we counted auctions using only the search keyword.

Replace:

$total_items = Flipnzee_Auction_Manager::count_auctions(
	$search
);

with:

$total_items = Flipnzee_Auction_Manager::count_auctions(
	$search,
	$status
);

Now the pagination count knows which status is currently selected.


Step 3 โ€“ Pass Status to get_all_auctions()

Next, update the method call that loads auction records.

Replace:

$this->items = Flipnzee_Auction_Manager::get_all_auctions(
	$per_page,
	$offset,
	$search,
	$orderby,
	$order
);

with:

$this->items = Flipnzee_Auction_Manager::get_all_auctions(
	$per_page,
	$offset,
	$search,
	$status,
	$orderby,
	$order
);

The status value is now available inside the database query.


Step 4 โ€“ Update get_all_auctions()

Open:

includes/class-auction-manager.php

Modify the function signature.

Replace:

public static function get_all_auctions(
	$per_page = 20,
	$offset = 0,
	$search = '',
	$orderby = 'created_at',
	$order = 'DESC'
)

with:

public static function get_all_auctions(
	$per_page = 20,
	$offset = 0,
	$search = '',
	$status = '',
	$orderby = 'created_at',
	$order = 'DESC'
)

The function can now receive the selected status.


Step 5 โ€“ Filter by Status

Inside get_all_auctions(), add SQL logic that filters records when a status is selected.

For example:

  • Draft โ†’ Draft auctions only
  • Active โ†’ Active auctions only
  • Closed โ†’ Closed auctions only

When no status is selected, the original query continues to return all auctions.


Step 6 โ€“ Support Search and Status Together

One important improvement is allowing both filters to work simultaneously.

Instead of choosing between Search or Status, the query now supports both.

Example:

SearchStatusResult
20DraftDraft auction containing “20”
LaptopActiveActive Laptop auctions
EmptyClosedAll Closed auctions
EmptyEmptyAll auctions

This creates a much better user experience.


Step 7 โ€“ Update count_auctions()

Next, update the counting method.

Change the function signature from:

public static function count_auctions(
	$search = ''
)

to:

public static function count_auctions(
	$search = '',
	$status = ''
)

Now the counting function receives the selected status as well.


Step 8 โ€“ Count Filtered Records Correctly

Update the SQL inside count_auctions() so it supports:

  • Search only
  • Status only
  • Search + Status
  • No filters

This ensures the total number of auctions displayed above the table always matches the records shown.

Without this change, the table could display five Draft auctions while the pagination still reported the total number of auctions in the database.


Step 9 โ€“ Test the Feature

Verify the following scenarios:

  • All Statuses
  • Draft
  • Active
  • Closed
  • Search only
  • Search + Draft
  • Search + Active
  • Search + Closed

Also verify that pagination continues to display the correct number of filtered records.


Final Result

After completing this lesson, the Auctions table supports:

  • โœ… Status filtering
  • โœ… Search
  • โœ… Search + Status
  • โœ… Pagination
  • โœ… Sorting
  • โœ… Bulk Actions
  • โœ… Bulk Delete

The Auctions management screen now behaves much more like WordPress core list tables, providing administrators with a smoother and more intuitive experience.


What We Learned

In this lesson, we learned how to:

  • Read filter values securely from the URL.
  • Pass filter values through multiple application layers.
  • Extend database methods with additional parameters.
  • Combine multiple filtering conditions safely.
  • Keep pagination synchronized with filtered data.
  • Build a more professional WordPress admin experience.

Download Source Code

Download the starting version of the plugin before the lesson:

โฌ‡ Download Plugin (After Lesson 31) (9 downloads )

Download the completed version after this lesson:

โฌ‡ Download Plugin (After Lesson 32) (6 downloads )

Next Lesson

In Lesson 33, we’ll extend our auction system by adding Start Date/Time and End Date/Time fields. These scheduling options will become the foundation for many future features, including countdown timers, automatic activation, automatic closing, winner selection, and Escrow.com integration.

Lesson 31 Implementation: Adding Status Filters to the Flipnzee Auctions Plugin

In the previous lessons, we enhanced the auction management table with searching, pagination, sortable columns, and bulk actions. As the number of auctions grows, administrators need an easier way to view only auctions in a particular state.

In this lesson, we’ll add a Status Filter that allows administrators to display only Draft, Active, or Closed auctions.


Step 1: Add a Status Filter Dropdown

The first step is to add a filter control above the auction table.

Open:

admin/class-admin.php

Locate the all_auctions_page() method and add a dropdown before the search box.

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

<select name="status">

	<option value="">All Statuses</option>

	<option
		value="draft"
		<?php selected( $status, 'draft' ); ?>
	>
		Draft
	</option>

	<option
		value="active"
		<?php selected( $status, 'active' ); ?>
	>
		Active
	</option>

	<option
		value="closed"
		<?php selected( $status, 'closed' ); ?>
	>
		Closed
	</option>

</select>

<?php

submit_button(
	'Filter',
	'secondary',
	'',
	false
);

This creates a familiar WordPress-style filter that remembers the selected option after the page reloads.


Step 2: Read the Selected Status

Next, the selected status needs to be available while preparing the table data.

Open:

admin/class-auctions-table.php

Inside the prepare_items() method, read the selected status.

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

The filter value is sanitized before being used anywhere else in the plugin.


Step 3: Pass the Filter to the Manager Class

The manager class is responsible for retrieving auction records from the database.

Update the method calls inside prepare_items().

$total_items = Flipnzee_Auction_Manager::count_auctions(
	$search,
	$status
);

Likewise, update the method that retrieves the auction list.

$this->items = Flipnzee_Auction_Manager::get_all_auctions(
	$per_page,
	$offset,
	$search,
	$status,
	$orderby,
	$order
);

This allows the selected filter to travel from the user interface to the database layer.


Step 4: Update the Manager Method Signatures

Open:

includes/class-auction-manager.php

Update the method definitions to accept the new $status parameter.

public static function get_all_auctions(
	$per_page = 20,
	$offset = 0,
	$search = '',
	$status = '',
	$orderby = 'created_at',
	$order = 'DESC'
)

Similarly, update the counting method.

public static function count_auctions(
	$search = '',
	$status = ''
)

Step 5: Apply the Status Filter to Database Queries

Modify the SQL queries so that they include the selected status whenever a filter has been chosen.

For example, if the administrator selects Draft, only auctions whose status is draft should be returned.

If All Statuses is selected, the query should continue returning every auction.

This approach keeps the filtering logic entirely inside the manager class.


Step 6: Update the Pagination Count

Pagination should display the number of filtered results instead of the total number of auctions.

For example:

  • 8 Draft auctions
  • 5 Active auctions
  • 12 Closed auctions

Each filtered view should have its own correct page count.


Step 7: Test the Feature

After uploading the updated plugin:

  1. Open Flipnzee Auctions โ†’ All Auctions.
  2. Select Draft from the Status dropdown.
  3. Click Filter.
  4. Confirm that only Draft auctions appear.
  5. Repeat the process for Active and Closed.
  6. Finally, choose All Statuses to display every auction again.

Also verify that:

  • Searching continues to work.
  • Sorting still functions correctly.
  • Pagination remains accurate.
  • Bulk actions continue working as expected.

Final Result

The auction management screen now includes a convenient Status filter that allows administrators to quickly narrow the list of auctions.

Instead of manually searching through every record, administrators can instantly display:

  • Draft auctions
  • Active auctions
  • Closed auctions
  • All auctions

This greatly improves usability, especially as the number of auction records grows.


What We Learned

In this lesson, we learned how to:

  • Add a custom filter control to a WordPress admin page.
  • Read filter values from user input safely.
  • Pass filter values through different layers of the plugin.
  • Update manager methods to support filtering.
  • Modify database queries based on selected filters.
  • Keep pagination accurate when filters are applied.
  • Preserve compatibility with searching, sorting, and bulk actions.

The Flipnzee Auctions plugin now provides an even more professional administration experience by allowing auction records to be filtered by status with just a few clicks.

Download Source Code

Download the starting version of the plugin before the lesson:

โฌ‡ Download Plugin (After Lesson 30) (18 downloads )

Download the completed version after this lesson:

โฌ‡ Download Plugin (After Lesson 31) (9 downloads )

Lesson 30 Implementation: Adding Bulk Delete Functionality to the Flipnzee Auctions Plugin

In Lesson 29, we added checkboxes and a Bulk Actions dropdown to the auction management table. Although the interface looked complete, selecting auctions and clicking Apply did not actually perform any action.

In this implementation, we’ll connect the user interface to the database so administrators can securely delete multiple auction records with a single click.


Step 1: Create a Method to Delete Multiple Auctions

The first task was to create a reusable method responsible for deleting multiple auction records from the database.

Inside includes/class-auction-manager.php, a new method named delete_multiple_auctions() was added.

public static function delete_multiple_auctions( $auction_ids ) {

	global $wpdb;

	$table = $wpdb->prefix . 'flipnzee_auctions';

	foreach ( $auction_ids as $auction_id ) {

		$wpdb->delete(
			$table,
			array(
				'id' => absint( $auction_id ),
			),
			array( '%d' )
		);
	}
}

Each submitted auction ID is sanitized using absint() before being passed to $wpdb->delete(), ensuring only valid numeric IDs are processed.


Step 2: Register a Bulk Action Processor

Next, the plugin needed a way to detect when an administrator clicks the Apply button.

Inside the constructor of admin/class-admin.php, a new action hook was registered.

add_action(
	'admin_init',
	array( $this, 'process_bulk_actions' )
);

The admin_init hook executes during every admin request, making it an ideal place to process submitted bulk actions before the page is rendered.


Step 3: Create the Bulk Action Handler

A new method named process_bulk_actions() was then added to the admin class.

The method performs several validation checks before deleting any records.

It verifies:

  • The current user has permission to manage auctions.
  • A bulk action has actually been submitted.
  • The selected action is Delete.
  • At least one auction has been selected.

If any of these conditions fail, the method immediately exits without making changes.


Step 4: Protect the Request with a WordPress Nonce

Security is an essential part of WordPress plugin development.

A nonce field was added to the bulk action form.

wp_nonce_field(
	'flipnzee_bulk_delete',
	'flipnzee_bulk_nonce'
);

This hidden field generates a unique security token that WordPress can later verify.


Step 5: Verify the Nonce

Inside process_bulk_actions(), the submitted nonce is validated before processing any deletion.

if (
	! isset( $_POST['flipnzee_bulk_nonce'] ) ||
	! wp_verify_nonce(
		sanitize_text_field(
			wp_unslash( $_POST['flipnzee_bulk_nonce'] )
		),
		'flipnzee_bulk_delete'
	)
) {
	return;
}

This prevents Cross-Site Request Forgery (CSRF) attacks by ensuring the request originated from the plugin’s own administration page.


Step 6: Retrieve the Selected Auction IDs

The selected auction IDs are submitted as an array.

Each value is sanitized before use.

$auction_ids = array_map(
	'absint',
	wp_unslash( $_POST['auction_ids'] )
);

Using array_map() ensures every submitted value becomes a valid integer.


Step 7: Delete the Selected Auctions

After validation, the manager method is called.

Flipnzee_Auction_Manager::delete_multiple_auctions(
	$auction_ids
);

This loops through every selected auction and removes it from the custom database table.


Step 8: Redirect Back to the Auction List

After completing the deletion, the administrator is redirected back to the auction listing page.

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

exit;

Using wp_safe_redirect() prevents accidental duplicate submissions if the page is refreshed.


Step 9: Display a Success Message

The existing success notification on the All Auctions page automatically detects the message=deleted query parameter and displays a confirmation message.

Auction deleted successfully.

This provides immediate feedback that the operation completed successfully.


Step 10: Test the Feature

After rebuilding and uploading the plugin, the feature was tested by:

  1. Selecting multiple auctions.
  2. Choosing Delete from the Bulk Actions dropdown.
  3. Clicking Apply.
  4. Confirming that the selected auctions disappeared.
  5. Verifying that the success message appeared.

The implementation worked exactly as expected.


Final Result

The auction management screen now supports professional bulk operations similar to WordPress core.

Administrators can:

  • Select one or many auctions.
  • Delete multiple records with a single action.
  • Receive immediate confirmation after deletion.
  • Benefit from WordPress nonce protection against unauthorized requests.

Combined with pagination, searching, sorting, and row actions implemented in previous lessons, the auction management screen now offers a polished administrative experience.


What We Learned

By completing this implementation, we learned how to:

  • Create reusable database methods for bulk operations.
  • Process submitted bulk actions in the WordPress admin area.
  • Register custom admin hooks.
  • Secure forms using WordPress nonces.
  • Sanitize arrays of submitted IDs.
  • Delete multiple database records safely.
  • Redirect after processing to prevent duplicate submissions.
  • Display success notifications following bulk operations.

With bulk deletion complete, the Flipnzee Auctions plugin now includes one of the most commonly used productivity features found in professional WordPress plugins.

Download Source Code

Download the starting version of the plugin before the lesson:

โฌ‡ Download Plugin (After Lesson 29) (14 downloads )

Download the completed version after this lesson:

โฌ‡ Download Plugin (After Lesson 30) (18 downloads )

 

Lesson 29 Implementation: Bulk Actions with WP_List_Table

Implementation Walkthrough

In this lesson, the goal was to allow administrators to select multiple auctions at once using checkboxes and prepare the table for bulk operations such as deleting multiple auctions simultaneously. WordPress provides built-in support for bulk actions in WP_List_Table, making this feature straightforward to implement.

Step 1: Add a Checkbox Column

The first step was to add a new checkbox column to the table.

Inside the get_columns() method of admin/class-auctions-table.php, a new column named cb was added as the first column.

public function get_columns() {

	return array(
		'cb'             => '<input type="checkbox" />',
		'id'             => 'ID',
		'listing_id'     => 'Listing ID',
		'start_price'    => 'Start Price',
		'reserve_price'  => 'Reserve Price',
		'buy_now_price'  => 'Buy Now Price',
		'status'         => 'Status',
		'created_at'     => 'Created At',
	);
}

The header checkbox allows administrators to quickly select or deselect every row displayed on the current page.


Step 2: Register the Bulk Actions

Next, the available bulk actions were registered by creating the get_bulk_actions() method.

public function get_bulk_actions() {

	return array(
		'delete' => 'Delete',
	);
}

At this stage only one actionโ€”Deleteโ€”was added, but additional actions such as Activate, Close Auction, or Export could easily be added later.


Step 3: Display a Checkbox for Every Auction

After registering the bulk actions, a new column_cb() method was added.

public function column_cb( $item ) {

	return sprintf(
		'<input type="checkbox" name="auction_ids[]" value="%d" />',
		absint( $item->id )
	);
}

This method tells WP_List_Table how to render the checkbox for each auction row.

Each checkbox stores the auction’s database ID, making it available when the administrator submits the bulk action form.


Step 4: Verify the Table Still Works

Before uploading the plugin, the PHP syntax was checked.

php -l admin/class-auctions-table.php

The output confirmed that no syntax errors existed.


Step 5: Upload the Updated Plugin

The plugin ZIP was rebuilt and uploaded to WordPress.

After activation, the All Auctions page immediately displayed:

  • A checkbox beside every auction
  • A “Select All” checkbox in the table header
  • A Bulk Actions dropdown
  • An Apply button

At this stage the Delete action did not yet perform any operationโ€”it simply prepared the interface for the next lesson.


Result

The auction management screen now behaves much more like WordPress’ built-in Posts, Pages, and Media screens.

Administrators can:

  • Select one auction
  • Select multiple auctions
  • Select all auctions on the current page
  • Choose a bulk action from the dropdown
  • Prepare to process multiple records with a single click

Although the Delete option is not functional yet, the user interface is now fully prepared for implementing bulk deletion in the next lesson.


What We Learned

By completing this lesson, we learned how to:

  • Add a checkbox column to a WP_List_Table
  • Register custom bulk actions
  • Create row checkboxes with column_cb()
  • Integrate WordPress’ built-in Bulk Actions dropdown
  • Prepare an admin table for processing multiple records efficiently

With this foundation in place, the next lesson will focus on processing the selected auctions and implementing a secure Bulk Delete feature using WordPress nonces and best practices.

Download Source Code

Download the starting version of the plugin before the lesson:

โฌ‡ Download Plugin (After Lesson 28) (12 downloads )

Download the completed version after this lesson:

โฌ‡ Download Plugin (After Lesson 29) (14 downloads )

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) (21 downloads )
  • Completed Project: Download the updated plugin after implementing the functional search feature and compare your changes if needed.
โฌ‡ Download Plugin (After Lesson 27) (20 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) (21 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.