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.

Lesson 20: Building the Edit Auction Form

In the previous lesson, we successfully created an Edit Auction page and retrieved the selected auction from the database. When an administrator clicked the Edit link, the plugin displayed the auction’s current information.

Although this proved that our retrieval logic worked correctly, the page was still read-only. In this lesson, we’ll replace the information table with a fully editable form whose fields are automatically populated with the auction’s existing values.

This approach mirrors how WordPress edits posts, pages, users, and many other objects. Administrators see the current values, make changes, and then save them.


Learning Objectives

By the end of this lesson you will be able to:

  • Build an editable administration form.
  • Pre-populate form fields with database values.
  • Display existing auction information inside HTML inputs.
  • Create the foundation for updating auctions in the next lesson.

Current Workflow

Our current workflow is:

All Auctions
      ↓
Click Edit
      ↓
Retrieve Auction
      ↓
Display Read-only Table

After today’s lesson it becomes:

All Auctions
      ↓
Click Edit
      ↓
Retrieve Auction
      ↓
Editable Form
      ↓
Save Changes (Next Lesson)

Step 1 – Locate the Edit Auction Page

Open:

admin/class-admin.php

Locate the method:

public function edit_auction_page()

Inside the method you’ll find a table displaying the auction details.


Step 2 – Replace the Table with a Form

Replace the existing table with the following form:

<form method="post">

	<table class="form-table">

		<tr>

			<th scope="row">
				<label for="listing_id">Listing ID</label>
			</th>

			<td>
				<input
					type="number"
					id="listing_id"
					name="listing_id"
					value="<?php echo esc_attr( $auction->listing_id ); ?>"
					required
					class="regular-text"
				>
			</td>

		</tr>

		<tr>

			<th scope="row">
				<label for="start_price">Start Price</label>
			</th>

			<td>
				<input
					type="number"
					step="0.01"
					id="start_price"
					name="start_price"
					value="<?php echo esc_attr( $auction->start_price ); ?>"
					class="regular-text"
				>
			</td>

		</tr>

		<tr>

			<th scope="row">
				<label for="reserve_price">Reserve Price</label>
			</th>

			<td>
				<input
					type="number"
					step="0.01"
					id="reserve_price"
					name="reserve_price"
					value="<?php echo esc_attr( $auction->reserve_price ); ?>"
					class="regular-text"
				>
			</td>

		</tr>

		<tr>

			<th scope="row">
				<label for="buy_now_price">Buy Now Price</label>
			</th>

			<td>
				<input
					type="number"
					step="0.01"
					id="buy_now_price"
					name="buy_now_price"
					value="<?php echo esc_attr( $auction->buy_now_price ); ?>"
					class="regular-text"
				>
			</td>

		</tr>

		<tr>

			<th scope="row">
				<label for="status">Status</label>
			</th>

			<td>

				<select
					name="status"
					id="status"
				>

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

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

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

				</select>

			</td>

		</tr>

	</table>

	<?php submit_button( 'Save Changes' ); ?>

</form>

Notice that every input field uses the current auction value. This allows administrators to modify existing information instead of re-entering everything.


Step 3 – Test the Plugin

Create a fresh ZIP and upload the updated plugin.

Navigate to:

Flipnzee Auctions → All Auctions

Click Edit.

Instead of a read-only table you should now see an editable form containing:

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

The Save Changes button will appear but won’t update the database yet. We’ll implement that functionality in the next lesson.


Why Build the Form First?

Professional software development often separates the user interface from the data processing logic.

Today’s lesson focuses entirely on presenting editable fields.

The next lesson will focus on validating user input and updating the database.

Separating these concerns makes the code easier to understand, test, and maintain.


Lesson Summary

In this lesson we transformed the Edit Auction page from a read-only display into an editable administration form.

Each input field is automatically populated using data retrieved from the database, allowing administrators to modify auction information without retyping existing values.

Although the Save Changes button is now visible, it does not yet perform any updates. That functionality will be implemented in the next lesson.


Key Takeaways

  • Editable forms improve administrator usability.
  • Existing values should always be pre-populated.
  • esc_attr() safely outputs values inside HTML attributes.
  • Building the interface before processing simplifies development.

Common Mistakes

  • Forgetting to pre-populate form values.
  • Using echo without esc_attr() inside HTML attributes.
  • Omitting the Status dropdown.
  • Trying to update the database before the form is complete.

Git Commands Used

git add .

git commit -m "Lesson 20: Build Edit Auction form"

git push

Testing Checklist

Before moving to the next lesson, verify that:

  • ✅ The plugin activates successfully.
  • ✅ The Edit page opens correctly.
  • ✅ All fields contain the current auction values.
  • ✅ Status is selected correctly.
  • ✅ The Save Changes button appears.
  • ✅ No PHP errors occur.

Project Status

✅ Dashboard

✅ Add Auction

✅ Save Auction

✅ View Auctions

✅ WP_List_Table

✅ Row Actions

✅ Edit Auction Page

✅ Edit Auction Form

⬜ Update Auction

⬜ Delete Auction

⬜ Bid Engine

⬜ Escrow Workflow

Developer’s Notebook

Creating the editing interface before implementing database updates follows a common development pattern. It allows you to verify that data retrieval and presentation work correctly before adding validation and persistence logic. This incremental approach also makes debugging significantly easier because each lesson introduces only one major concept.

Lesson 19: Creating the Edit Auction Page

Introduction

In the previous lesson, we enhanced our auction list by adding View, Edit, and Delete row actions beneath each Auction ID. Although these links improved the user interface, they were only placeholders.

In this lesson, we’ll make the Edit action functional by creating a dedicated Edit Auction page inside the WordPress administration area. Initially, this page will retrieve an auction from the database and display its existing information in a form. We’ll save the changes in the next lesson.

Breaking the workflow into two lessons makes it easier to understand and follows the same incremental approach we’ve used throughout this series.


Learning Objectives

By the end of this lesson, you’ll be able to:

  • Build an Edit Auction administration page.
  • Pass an auction ID through a URL.
  • Retrieve a single auction from the database.
  • Populate a form with existing values.
  • Understand how editing workflows begin in WordPress plugins.

How the Workflow Changes

Our administration flow now becomes:

All Auctions
      ↓
Click Edit
      ↓
Edit Auction Page
      ↓
Retrieve Auction
      ↓
Display Existing Values

Notice that we’re still not saving changes yet. Today’s goal is to display the current data.


Step 1 – Update the Edit Row Action

Open:

admin/class-auctions-table.php

Locate the column_id() method.

Replace the Edit action with:

'edit' => sprintf(
	'<a href="%s">Edit</a>',
	admin_url(
		'admin.php?page=flipnzee-edit-auction&auction_id=' . absint( $item->id )
	)
),

Now every Edit link will open the correct auction.


Step 2 – Register a New Submenu

Open:

admin/class-admin.php

Inside register_menu(), add another submenu:

add_submenu_page(
	'flipnzee-auctions',
	'Edit Auction',
	'Edit Auction',
	'manage_options',
	'flipnzee-edit-auction',
	array( $this, 'edit_auction_page' )
);

This page won’t normally appear in the menu because administrators will access it through the Edit link.


Step 3 – Create the Edit Page

Inside class-admin.php, create:

public function edit_auction_page() {

	$auction_id = isset( $_GET['auction_id'] )
		? absint( $_GET['auction_id'] )
		: 0;

	$auction = Flipnzee_Auction_Manager::get_auction(
		$auction_id
	);

	if ( ! $auction ) {

		echo '<div class="notice notice-error"><p>Auction not found.</p></div>';

		return;
	}

	?>

	<div class="wrap">

		<h1>Edit Auction</h1>

		<p>The auction was loaded successfully.</p>

	</div>

	<?php
}

At this stage, we’re simply verifying that the selected auction can be retrieved.


Step 4 – Test the Plugin

Create a fresh ZIP.

Upload the plugin.

Navigate to:

Flipnzee Auctions → All Auctions

Click Edit beneath any auction.

If everything is working correctly, you’ll see:

Edit Auction

The auction was loaded successfully.

This confirms that:

  • The row action works.
  • The auction ID is passed correctly.
  • The Auction Manager retrieves the correct record.
  • The administration page loads successfully.

Why Stop Here?

Many beginners try to retrieve, display, validate, and save data in a single lesson.

Breaking the process into smaller steps makes debugging much easier.

Today we verify that the correct auction is loaded.

In the next lesson, we’ll build the complete editing form.


Lesson Summary

In this lesson, we transformed the placeholder Edit link into a working navigation path.

Administrators can now select an auction from the list, open an Edit Auction page, and retrieve the corresponding record from the database.

Although editing isn’t complete yet, we’ve successfully implemented the first half of the editing workflow.


Key Takeaways

  • ✓ Row actions can pass record IDs through URLs.
  • ✓ Retrieve individual records using the Auction Manager.
  • ✓ Separate retrieval from saving.
  • ✓ Build editing workflows incrementally.

Common Mistakes

  • Forgetting to sanitize the auction ID.
  • Accessing the database directly instead of using the Auction Manager.
  • Assuming every ID exists.
  • Trying to save changes before displaying the existing values.

Git Commands Used

git add .

git commit -m "Lesson 19: Create Edit Auction page"

git push

Testing Checklist

Before continuing:

  • ✅ Plugin activates successfully.
  • ✅ All Auctions page still loads.
  • ✅ Every Edit link opens a new page.
  • ✅ Correct auction ID is passed.
  • ✅ Auction record is retrieved successfully.
  • ✅ “Auction not found” appears only for invalid IDs.

Project Status

✅ Dashboard

✅ Add Auction

✅ Save Auctions

✅ View Auctions

✅ WP_List_Table

✅ Row Actions

✅ Edit Auction Page

⬜ Edit Auction Form

⬜ Save Edited Auction

⬜ Delete Auction

⬜ Bid Engine

⬜ Escrow Workflow

Project Evolution

With the introduction of the Edit Auction page, Flipnzee Auctions now supports navigation from a list of records to an individual record. This pattern is common in many WordPress plugins and content management systems. By retrieving a single auction before attempting to modify it, we’ve established a clean editing workflow that can be expanded safely in future lessons.


Developer’s Notebook

Professional applications rarely perform retrieval, validation, and persistence in one step. Separating these responsibilities improves readability, simplifies testing, and makes future enhancements much easier. Today’s lesson focuses entirely on retrieving the correct record, allowing the next lesson to concentrate solely on editing and saving changes.

Lesson 16: Building a Professional Auction List with WP_List_Table

Introduction

In the previous lesson, we displayed all auctions inside a simple HTML table. While this approach works well for learning, professional WordPress plugins usually rely on a built-in class called WP_List_Table.

WP_List_Table powers many of the tables you already use every day in WordPress, including Posts, Pages, Comments, Plugins, Themes, and Users.

In this lesson, we’ll replace our simple table with a WP_List_Table implementation, giving our plugin a more professional and scalable administration interface.


Learning Objectives

By the end of this lesson, you’ll be able to:

  • Understand the purpose of WP_List_Table.
  • Create your first custom table class.
  • Display auction records using WordPress’s native table layout.
  • Prepare the plugin for pagination, searching, sorting, and bulk actions.
  • Build a more professional administration interface.

Why Replace Our HTML Table?

Our current table works:

Database

↓

Auction Manager

↓

HTML Table

However, WordPress already provides a reusable table framework.

Using WP_List_Table, our architecture becomes:

Database

↓

Auction Manager

↓

WP_List_Table

↓

WordPress Admin

This gives us a consistent look and makes future enhancements much easier.


What Is WP_List_Table?

WP_List_Table is an internal WordPress class responsible for rendering tables in the administration area.

It provides support for:

  • Pagination
  • Sorting columns
  • Bulk actions
  • Row actions
  • Search boxes
  • Screen options

Many popular plugins extend this class to create professional management screens.


Step 1 – Create a New File

Create:

admin/class-auctions-table.php

This class will extend WP_List_Table.


Step 2 – Load the WordPress Class

At the top of the file, add:

if ( ! class_exists( 'WP_List_Table' ) ) {
	require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}

This ensures the base class is available.


Step 3 – Create the Table Class

Begin with:

class Flipnzee_Auctions_Table extends WP_List_Table {

}

Every custom list table extends the WordPress base class.


Step 4 – Define the Columns

Our first version will display:

ColumnDescription
IDAuction ID
ListingListing ID
Start PriceOpening bid
Reserve PriceMinimum acceptable price
Buy NowImmediate purchase price
StatusCurrent auction status

Additional columns will be introduced in later lessons.


Step 5 – Populate the Table

Instead of writing SQL inside the table class, retrieve data using:

Flipnzee_Auction_Manager::get_all_auctions();

The Auction Manager continues to own all database operations.


Step 6 – Replace the Manual Table

The All Auctions page will eventually become as simple as:

$table = new Flipnzee_Auctions_Table();

$table->prepare_items();

$table->display();

Notice how much cleaner the administration page becomes.


Why This Architecture Matters

Rather than mixing HTML, SQL, and business logic together, each layer performs one task.

Database

↓

Auction Manager

↓

WP_List_Table

↓

Admin Page

↓

Administrator

As our plugin grows, this separation will make new features much easier to implement.


Lesson Summary

In this lesson, we introduced the WP_List_Table class, the same framework WordPress uses throughout its administration area.

Although our implementation is still simple, this architectural improvement prepares Flipnzee Auctions for advanced capabilities such as searching, sorting, pagination, row actions, and bulk operations.


Key Takeaways

  • WP_List_Table is WordPress’s standard table framework.
  • ✓ Keep SQL inside the Auction Manager.
  • ✓ Separate presentation from business logic.
  • ✓ Build interfaces using WordPress conventions.
  • ✓ Prepare early for future scalability.

Common Mistakes

  • Writing SQL inside the table class.
  • Duplicating business logic.
  • Mixing HTML with database queries.
  • Ignoring WordPress’s existing UI framework.

Git Commands Used

git add .

git commit -m "Lesson 16: Introduce WP_List_Table"

git push

Project Status

✅ Development environment

✅ Plugin skeleton

✅ Plugin lifecycle

✅ Database layer

✅ Auction Manager

✅ Dashboard

✅ Add Auction

✅ Save auctions

✅ Display auctions

✅ WP_List_Table architecture

⬜ Search auctions

⬜ Sort auctions

⬜ Bulk actions

⬜ Edit auction

⬜ Delete auction

⬜ Bid engine

⬜ Escrow workflow

⬜ Version 1.0

Project Evolution

Until now, our auction list was rendered using a manually constructed HTML table. While that approach is perfectly suitable for learning, it doesn’t take advantage of WordPress’s native administration framework.

By introducing WP_List_Table, we’re aligning Flipnzee Auctions with the same patterns used throughout WordPress itself. This change not only improves consistency but also lays the groundwork for features such as pagination, searching, sorting, row actions, and bulk operations without redesigning the administration interface later.


Developer’s Notebook

One of the strengths of WordPress is that it provides reusable components for common administration tasks. Whenever possible, it’s better to build on those components rather than reinventing them. WP_List_Table is a good example: instead of maintaining our own table system, we can leverage a mature framework that’s already familiar to WordPress users and designed to scale as our plugin grows.


Looking Ahead

In Lesson 17, we’ll make our custom WP_List_Table fully functional by displaying real auction data and introducing row actions such as View, Edit, and Delete, bringing the administration interface even closer to the native WordPress experience.

Lesson 17: Migrating the Auction List to WP_List_Table

Introduction

In the previous lesson, we learned what WP_List_Table is and why professional WordPress plugins use it instead of manually building HTML tables.

In this lesson, we’ll complete the migration by creating our own WP_List_Table class, loading it into the plugin, and updating the All Auctions page to use it.

Although the table will initially display the same information as before, the underlying architecture will be much more scalable and will prepare us for pagination, sorting, searching, and row actions in future lessons.


Learning Objectives

By the end of this lesson, you’ll be able to:

  • Create a custom WP_List_Table.
  • Load the new table class into your plugin.
  • Replace a manually created HTML table.
  • Display auction records using WordPress’s native administration framework.

Step 1 – Create the Table Class

Create a new file:

admin/class-auctions-table.php

Copy the complete code from this lesson into the file.

This class extends WordPress’s WP_List_Table and is responsible for displaying auction records.


Step 2 – Load the Table Class

Open:

flipnzee-auctions.php

Immediately below the section that loads the Admin Posts Class, add:

/**
 * Load Auctions Table Class
 */
if ( file_exists( FLIPNZEE_AUCTION_PATH . 'admin/class-auctions-table.php' ) ) {
	require_once FLIPNZEE_AUCTION_PATH . 'admin/class-auctions-table.php';
}

Your loading order should now be:

  1. Loader
  2. Database
  3. Auction Manager
  4. Admin Class
  5. Admin Posts Class
  6. Auctions Table Class

Keeping related classes grouped together makes the plugin easier to maintain.


Step 3 – Update the Admin Page

Open:

admin/class-admin.php

Locate the all_auctions_page() method.

Replace the existing HTML table with:

$table = new Flipnzee_Auctions_Table();

$table->prepare_items();

$table->display();

Your page now becomes responsible only for displaying the page header and calling the table class.


Step 4 – Test the Plugin

Create a fresh ZIP.

Upload it to your WordPress website.

Activate the plugin.

Navigate to:

Flipnzee Auctions → All Auctions

If everything has been configured correctly, your auctions should now be displayed using your custom WP_List_Table.


Understanding the New Architecture

Our plugin now follows this flow:

Database
      ↓
Auction Manager
      ↓
WP_List_Table
      ↓
Admin Page
      ↓
Administrator

Notice that the Admin page no longer knows how to build the table.

Instead, it delegates that responsibility to Flipnzee_Auctions_Table.


Why This Is Better

Compared to our previous implementation:

  • The admin page contains much less code.
  • Table rendering is reusable.
  • Future enhancements become much easier.
  • The plugin follows WordPress conventions more closely.

This architecture will allow us to add pagination, searching, sorting, bulk actions, and row actions without redesigning the administration page.


Lesson Summary

In this lesson, we completed the migration from a manually constructed HTML table to WordPress’s WP_List_Table framework.

Although the interface appears familiar, the plugin now uses a more professional architecture that separates presentation from business logic and prepares the administration area for future enhancements.


Key Takeaways

  • ✓ Create a dedicated WP_List_Table class.
  • ✓ Load the class in flipnzee-auctions.php.
  • ✓ Replace the manual HTML table.
  • ✓ Keep presentation separate from business logic.
  • ✓ Build on WordPress’s native administration framework.

Common Mistakes

  • Forgetting to load class-auctions-table.php.
  • Calling $table->display() without first calling prepare_items().
  • Leaving the old HTML table in class-admin.php.
  • Writing SQL inside the table class.

Git Commands Used

git add .

git commit -m "Lesson 17: Migrate auction list to WP_List_Table"

git push

Project Status

✅ Dashboard

✅ Add Auction

✅ Save Auction

✅ View Auctions

✅ Migrate to WP_List_Table

⬜ Pagination

⬜ Search

⬜ Sorting

⬜ Row Actions

⬜ Edit Auction

⬜ Delete Auction

⬜ Bid Engine

⬜ Escrow Workflow

Developer’s Notebook

One of the advantages of building on WordPress’s native components is that your plugin becomes easier for other WordPress developers to understand. By moving table rendering into a dedicated WP_List_Table class, we’ve reduced the responsibilities of the admin page and laid the foundation for advanced features without changing the overall architecture again.

Lesson 14: Completing Your First End-to-End WordPress Plugin Workflow

Introduction

In the previous lesson, we created a dedicated form handler using the WordPress Admin Post API. The form was successfully submitted, the nonce was verified, and WordPress redirected the administrator back to the Add Auction page.

However, no auction was actually created.

In this lesson, we’ll complete the workflow by validating the submitted data, calling the Auction Manager, inserting the auction into the database, and displaying a confirmation message.

This is the first time every layer of our plugin works together.


Learning Objectives

By the end of this lesson, you’ll be able to:

  • Sanitize submitted data.
  • Call the Auction Manager from the form handler.
  • Insert records into the database.
  • Redirect users with status messages.
  • Display confirmation notices.
  • Complete your first end-to-end plugin workflow.

Our Complete Workflow

Administrator

↓

Add Auction Form

↓

admin-post.php

↓

Nonce Verification

↓

Data Validation

↓

Auction Manager

↓

Database

↓

Redirect

↓

Success Message

Every component now has a clearly defined responsibility.


Step 1 – Process Submitted Data

Open:

admin/class-admin-posts.php

Replace the comment:

/*
 * Form processing will be added
 * in Lesson 14.
 */

with:

$listing_id = absint( $_POST['listing_id'] );

$start_price = (float) $_POST['start_price'];

$reserve_price = (float) $_POST['reserve_price'];

$buy_now_price = (float) $_POST['buy_now_price'];

Step 2 – Create the Auction

Immediately below the previous code, add:

$auction_id = Flipnzee_Auction_Manager::create_auction(
	$listing_id,
	$start_price,
	$reserve_price,
	$buy_now_price
);

The Form Handler doesn’t communicate directly with the database.

Instead, it delegates that responsibility to the Auction Manager.


Step 3 – Redirect with Status

Replace:

wp_safe_redirect(
	admin_url( 'admin.php?page=flipnzee-add-auction' )
);

with:

if ( $auction_id ) {

	wp_safe_redirect(
		admin_url(
			'admin.php?page=flipnzee-add-auction&message=success'
		)
	);

} else {

	wp_safe_redirect(
		admin_url(
			'admin.php?page=flipnzee-add-auction&message=error'
		)
	);

}

exit;

Step 4 – Display the Message

Open:

admin/class-admin.php

Inside add_auction_page(), immediately before:

<div class="wrap">

add:

if ( isset( $_GET['message'] ) ) {

	if ( 'success' === $_GET['message'] ) {

		echo '<div class="notice notice-success is-dismissible"><p>Auction created successfully.</p></div>';

	}

	if ( 'error' === $_GET['message'] ) {

		echo '<div class="notice notice-error is-dismissible"><p>Unable to create auction.</p></div>';

	}
}

Step 5 – Test the Plugin

Create a fresh ZIP.

Upload the plugin.

Open:

Flipnzee Auctions → Add Auction

Enter:

  • Listing ID: 1
  • Start Price: 100
  • Reserve Price: 150
  • Buy Now Price: 300

Click:

Create Auction

You should now see:

Auction created successfully.

Step 6 – Verify the Database

Open phpMyAdmin.

Browse:

wp_flipnzee_auctions

Instead of zero rows, you should now see your first auction record.

Return to the plugin dashboard.

The Total Auctions counter should now display:

1

Congratulations! Your plugin has completed its first full workflow.


Lesson Summary

In this lesson, we completed the first end-to-end workflow of Flipnzee Auctions.

Administrators can now submit auction information through the WordPress Dashboard. The request is securely processed, validated, passed to the Auction Manager, stored in the database, and confirmed with a success message.

This marks the point where the plugin becomes genuinely functional.


Key Takeaways

  • ✓ Separate form processing from page rendering.
  • ✓ Sanitize submitted data.
  • ✓ Let the Auction Manager handle database operations.
  • ✓ Redirect after processing.
  • ✓ Display administrator-friendly status messages.

Common Mistakes

  • Accessing $_POST without validation.
  • Writing SQL inside the form handler.
  • Forgetting to redirect after processing.
  • Displaying raw database errors to users.

Git Commands Used

git add .

git commit -m "Lesson 14: Complete first end-to-end workflow"

git push

Project Status

✅ Development environment

✅ Plugin skeleton

✅ Plugin installation

✅ Plugin lifecycle

✅ .gitignore

✅ Data architecture

✅ Database table

✅ Auction Manager

✅ Retrieve auction records

✅ WordPress admin menu

✅ Plugin dashboard

✅ Add Auction page

✅ Secure form architecture

✅ Admin Post handler

✅ First end-to-end workflow

⬜ Display all auctions

⬜ Edit auctions

⬜ Delete auctions

⬜ Bid engine

⬜ Escrow workflow

⬜ Version 1.0

Project Evolution

Earlier lessons focused on building the plugin’s foundation. This lesson connects those individual pieces into a complete workflow. The administrator no longer interacts directly with the database; instead, each layer performs a specific role, resulting in a cleaner and more maintainable architecture.

This same pattern will be reused for editing auctions, deleting auctions, managing bids, and handling Escrow.com transactions.


Developer’s Notebook

One of the best indicators of a well-designed application is the separation of responsibilities. In Flipnzee Auctions, the form collects information, the Form Handler processes requests, the Auction Manager performs business logic, and the Database class manages storage. Because these responsibilities are clearly separated, the plugin can continue to grow without becoming difficult to understand or maintain.


Looking Ahead

In Lesson 15, we’ll display all saved auctions inside the WordPress Dashboard using a professional table layout. Instead of checking phpMyAdmin, administrators will be able to browse, review, and eventually edit auction records directly from the plugin interface.

Lesson 15: Displaying All Auctions in the WordPress Dashboard

Introduction

In the previous lesson, we completed our first end-to-end workflow by allowing administrators to create auctions directly from the WordPress Dashboard.

Although the auction is successfully stored in the database, administrators still need phpMyAdmin to verify the record.

Professional plugins display data inside the WordPress Dashboard instead of requiring direct database access.

In this lesson, we’ll create our first Auction List page that retrieves every auction from the database and displays it in a clean WordPress table.


Learning Objectives

By the end of this lesson, you’ll be able to:

  • Create another WordPress admin page.
  • Display data retrieved from the database.
  • Loop through auction records.
  • Build an HTML table using WordPress admin styling.
  • Understand how data flows from the database to the user interface.

What We’ll Build

Our admin menu will now become:

Flipnzee Auctions
│
├── Dashboard
├── Add Auction
└── All Auctions

Instead of checking phpMyAdmin, administrators will browse auctions directly inside WordPress.


Why Create an Auction List?

Applications rarely interact directly with databases.

Instead:

Database

↓

Auction Manager

↓

Admin Page

↓

Administrator

The Auction Manager retrieves the data.

The Admin page displays it.

Each component performs one responsibility.


Step 1 – Add a New Submenu

Open:

admin/class-admin.php

Inside register_menu() add another submenu:

add_submenu_page(
	'flipnzee-auctions',
	'All Auctions',
	'All Auctions',
	'manage_options',
	'flipnzee-all-auctions',
	array( $this, 'all_auctions_page' )
);

Step 2 – Create the Page

Inside the class add:

public function all_auctions_page() {

	$auctions = Flipnzee_Auction_Manager::get_all_auctions();

	?>

	<div class="wrap">

		<h1>All Auctions</h1>

		<table class="widefat striped">

			<thead>

				<tr>

					<th>ID</th>
					<th>Listing</th>
					<th>Start Price</th>
					<th>Reserve</th>
					<th>Buy Now</th>
					<th>Status</th>

				</tr>

			</thead>

			<tbody>

			<?php if ( ! empty( $auctions ) ) : ?>

				<?php foreach ( $auctions as $auction ) : ?>

					<tr>

						<td><?php echo esc_html( $auction->id ); ?></td>

						<td><?php echo esc_html( $auction->listing_id ); ?></td>

						<td><?php echo esc_html( $auction->start_price ); ?></td>

						<td><?php echo esc_html( $auction->reserve_price ); ?></td>

						<td><?php echo esc_html( $auction->buy_now_price ); ?></td>

						<td><?php echo esc_html( ucfirst( $auction->status ) ); ?></td>

					</tr>

				<?php endforeach; ?>

			<?php else : ?>

				<tr>

					<td colspan="6">

						No auctions found.

					</td>

				</tr>

			<?php endif; ?>

			</tbody>

		</table>

	</div>

	<?php
}

Step 3 – Test the Plugin

Create another auction.

Open:

Flipnzee Auctions → All Auctions

You should now see every auction displayed in a professional WordPress table.

You no longer need phpMyAdmin to verify your data.


Understanding the Flow

The page never communicates directly with SQL.

Instead:

Database

↓

Auction Manager

↓

PHP Array

↓

foreach()

↓

HTML Table

This separation keeps the code organized and maintainable.


Lesson Summary

In this lesson, we created our first data listing page.

Administrators can now browse auctions directly inside the WordPress Dashboard without using phpMyAdmin.

This marks another important milestone because the plugin now supports both creating and viewing auctions.


Key Takeaways

  • ✓ Retrieve data through the Auction Manager.
  • ✓ Display records using foreach.
  • ✓ Escape all output with esc_html().
  • ✓ Use WordPress admin table styling.
  • ✓ Keep presentation separate from business logic.

Common Mistakes

  • Writing SQL inside the admin page.
  • Forgetting to escape output.
  • Not handling an empty database.
  • Mixing HTML with business logic unnecessarily.

Git Commands Used

git add .

git commit -m "Lesson 15: Display all auctions"

git push

Project Status

✅ Plugin dashboard

✅ Add Auction

✅ Secure form processing

✅ Save auction

✅ Display all auctions

⬜ View auction details

⬜ Edit auction

⬜ Delete auction

⬜ Bid engine

⬜ Escrow workflow

⬜ Version 1.0

Project Evolution

Our plugin has now reached the point where administrators can both create and browse auction records entirely within the WordPress Dashboard.

As the project continues, we’ll build on this interface by adding actions such as editing, deleting, filtering, sorting, and eventually managing bids and escrow transactions. Each new feature will reuse the architecture we’ve established, making the application easier to maintain and extend.


Developer’s Notebook

Although the HTML table works well for learning purposes, many professional plugins eventually migrate to WordPress’s WP_List_Table class. We’ll continue with a simple table for now because it clearly demonstrates how data is retrieved and displayed. Once the plugin becomes more advanced, we’ll refactor the interface to use WP_List_Table for features such as pagination, bulk actions, sorting, and searching.

Lesson 13: Creating a Dedicated Form Handler with the WordPress Admin Post API

Introduction

In the previous lesson, we improved the architecture of our Add Auction form by submitting it to WordPress’s admin-post.php endpoint and protecting it with a nonce.

However, there is still no code that actually receives the submitted form.

In this lesson, we’ll create a dedicated Form Handler class. This class will verify the nonce, validate the submitted data, and then call the Auction Manager to create the auction.

By keeping form processing separate from page rendering, we make the plugin cleaner, easier to maintain, and more aligned with WordPress best practices.


Learning Objectives

By the end of this lesson, you’ll be able to:

  • Understand the WordPress Admin Post API.
  • Register custom admin actions.
  • Create a dedicated form handler class.
  • Verify WordPress nonces.
  • Validate submitted form data.
  • Prepare the plugin for database insertion.

Why Create a Form Handler?

Instead of mixing everything together:

Form

↓

SQL

↓

HTML

we’ll separate responsibilities:

Form

↓

Form Handler

↓

Auction Manager

↓

Database

Each component now has one responsibility.


Step 1 – Create a New File

Inside the admin folder create:

class-admin-posts.php

Step 2 – Create the Form Handler

Copy the following code into the file.

<?php

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class Flipnzee_Auction_Admin_Posts {

	/**
	 * Constructor.
	 */
	public function __construct() {

		add_action(
			'admin_post_flipnzee_create_auction',
			array( $this, 'handle_create_auction' )
		);
	}

	/**
	 * Handle Add Auction form submission.
	 */
	public function handle_create_auction() {

		// Verify nonce.
		check_admin_referer(
			'flipnzee_create_auction',
			'flipnzee_nonce'
		);

		// Form processing will be added in Lesson 14.

		wp_safe_redirect(
			admin_url( 'admin.php?page=flipnzee-add-auction' )
		);

		exit;
	}
}

new Flipnzee_Auction_Admin_Posts();

Understanding admin_post

WordPress automatically looks for an action matching:

admin_post_{action}

Our form contains:

<input
	type="hidden"
	name="action"
	value="flipnzee_create_auction">

WordPress therefore executes:

admin_post_flipnzee_create_auction

which calls our handler.


Understanding check_admin_referer()

This function verifies the nonce we added in Lesson 12.

If the nonce is invalid:

  • Processing stops immediately.
  • WordPress displays an error.
  • The request is rejected.

This protects our plugin against Cross-Site Request Forgery (CSRF) attacks.


Why Redirect?

After processing the form, we redirect back to the Add Auction page.

This prevents duplicate submissions if the administrator refreshes the page and follows the standard Post/Redirect/Get (PRG) pattern used by professional web applications.


Step 3 – Load the New Class

Open:

flipnzee-auctions.php

Immediately after loading the Admin class, add:

/**
 * Load Admin Posts Class
 */
if ( file_exists( FLIPNZEE_AUCTION_PATH . 'admin/class-admin-posts.php' ) ) {
	require_once FLIPNZEE_AUCTION_PATH . 'admin/class-admin-posts.php';
}

Step 4 – Test the Plugin

Create a new ZIP.

Upload the plugin.

Activate it.

Open:

Flipnzee Auctions → Add Auction

Complete the form.

Click:

Create Auction

Nothing will be saved yet.

However, if everything has been configured correctly, the form should redirect back to the Add Auction page without errors.

That’s exactly what we want at this stage.


Lesson Summary

In this lesson, we created a dedicated Form Handler using the WordPress Admin Post API.

Although the handler currently verifies the nonce and redirects the user, it establishes the architecture we’ll use for all future form processing.

In the next lesson, we’ll finally connect this handler to the Auction Manager and insert our first auction into the database.


Key Takeaways

  • ✓ Use admin_post for processing administration forms.
  • ✓ Verify nonces before processing data.
  • ✓ Separate form handling from page rendering.
  • ✓ Redirect after successful processing.
  • ✓ Build applications one layer at a time.

Common Mistakes

  • Forgetting to register the admin_post action.
  • Processing forms inside page-rendering methods.
  • Omitting nonce verification.
  • Forgetting to call exit after wp_safe_redirect().

Git Commands Used

git add .

git commit -m "Lesson 13: Create Admin Post form handler"

git push

Project Status

✅ Plugin dashboard

✅ Add Auction page

✅ Secure form architecture

✅ Admin Post handler

⬜ Save auction

⬜ Display auctions

⬜ Edit auction

⬜ Bid engine

⬜ Escrow workflow

⬜ Version 1.0

Project Evolution

Our plugin now follows a cleaner architecture by separating administration pages from form processing. This makes future features such as editing auctions, deleting auctions, and managing bids much easier to implement because every form can follow the same pattern.

As the project continues to grow, this separation of concerns will keep the codebase organized and easier to maintain.


Developer’s Notebook

One of the defining characteristics of well-designed WordPress plugins is that user interfaces and request processing are kept separate. While beginners often process forms directly inside page-rendering methods, larger plugins typically use dedicated handlers that can be reused, tested, and extended independently. Adopting this pattern early prepares the project for long-term growth and makes the code easier for other developers to understand.

Lesson 12: Processing WordPress Admin Forms the Professional Way

Introduction

In the previous lesson, we created our first Add Auction page inside the WordPress Dashboard. Although administrators can now fill in auction details, the form doesn’t yet save any information.

There are several ways to process WordPress forms. For small plugins, it’s common to process the submitted data inside the same function that displays the form. While this works, it mixes presentation with business logic.

In this lesson, we’ll adopt a more professional approach by using WordPress’s Admin Post API. This separates form processing from page rendering and produces cleaner, more maintainable code.

By the end of this lesson, administrators will be able to submit an auction through the dashboard, and the Auction Manager will store it in the database.


Learning Objectives

By the end of this lesson, you’ll be able to:

  • Understand the WordPress Admin Post API.
  • Protect forms with WordPress nonces.
  • Process form submissions in a dedicated handler.
  • Validate and sanitize submitted data.
  • Call the Auction Manager to save data.
  • Redirect users safely after processing.

Why Change Our Architecture?

A common beginner approach looks like this:

Display Form
      │
      ▼
Read $_POST
      │
      ▼
Save Database

This mixes multiple responsibilities inside one function.

Instead, we’ll use:

Display Form
      │
      ▼
admin-post.php
      │
      ▼
Form Handler
      │
      ▼
Auction Manager
      │
      ▼
Database

Each component now has one clearly defined responsibility.


What We’ll Build

Our plugin will now consist of:

  • Admin Class – Registers menus and displays pages.
  • Auction Manager – Handles auction business logic.
  • Form Handler – Processes submitted forms.
  • Database Class – Creates and manages database tables.

This separation makes the plugin easier to maintain as it grows.


Step 1 – Add a Nonce to the Form

Every administrative form should include a nonce.

Immediately before the submit button, add:

<?php wp_nonce_field(
	'flipnzee_create_auction',
	'flipnzee_nonce'
); ?>

This generates a hidden security token that WordPress verifies when the form is submitted.


Step 2 – Submit to admin-post.php

Instead of submitting the form back to the same page, we’ll submit it to WordPress.

Our form will eventually look like this:

<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">

We’ll also include a hidden field named:

<input
	type="hidden"
	name="action"
	value="flipnzee_create_auction">

The value of this field tells WordPress which handler should process the request.


Step 3 – Create a Form Handler

Rather than placing processing code inside the page-rendering method, we’ll create a dedicated handler class.

This handler will:

  • Verify the nonce.
  • Validate the submitted data.
  • Call the Auction Manager.
  • Redirect back to the dashboard.

This keeps the user interface completely separate from the business logic.


Step 4 – Validate and Sanitize Input

Before saving anything, we’ll clean the submitted data.

Examples include:

  • absint() for IDs.
  • (float) for prices.
  • sanitize_text_field() for text values.
  • wp_unslash() when appropriate.

Never trust raw $_POST values.


Step 5 – Save the Auction

Once the data has been validated, we’ll call:

Flipnzee_Auction_Manager::create_auction();

Notice that the form handler doesn’t execute SQL directly.

The Auction Manager remains responsible for interacting with the database.


Step 6 – Redirect Back

After processing the form, the handler redirects the administrator back to the Add Auction page.

This prevents duplicate submissions if the page is refreshed and follows the standard WordPress Post/Redirect/Get pattern.


Why This Is Better

Separating page rendering from form processing provides several advantages:

  • Cleaner code.
  • Easier debugging.
  • Better security.
  • Easier testing.
  • Simpler future enhancements.

As the plugin grows, we’ll reuse this same architecture for editing auctions, deleting auctions, placing bids, and updating settings.


Lesson Summary

In this lesson, we redesigned the way our plugin handles administration forms.

Rather than processing submitted data inside the page-rendering method, we adopted the WordPress Admin Post API. This keeps the user interface, form processing, and business logic separated into dedicated components.

Although this approach requires a little more setup, it scales much better as the plugin becomes more sophisticated.


Key Takeaways

  • ✓ Use WordPress nonces for every administration form.
  • ✓ Process forms through admin-post.php.
  • ✓ Keep HTML separate from business logic.
  • ✓ Validate and sanitize all user input.
  • ✓ Let the Auction Manager handle database operations.

Common Mistakes

  • Processing $_POST directly inside the view.
  • Forgetting nonce verification.
  • Trusting user input without validation.
  • Mixing SQL with HTML.

Git Commands Used

git add .

git commit -m "Lesson 12: Process admin forms using Admin Post API"

git push

Project Status

✅ Plugin dashboard

✅ Add Auction page

✅ Secure form architecture

⬜ Form handler

⬜ Save auction

⬜ Auction listing

⬜ Edit auction

⬜ Bid engine

⬜ Escrow workflow

Project Evolution

Earlier in this series, our focus was simply getting a working plugin. As the project has matured, we’ve begun adopting architectural patterns used in production-quality WordPress plugins.

Introducing the Admin Post API is one such refinement. Although it requires slightly more code, it provides a cleaner separation of responsibilities and lays the foundation for a plugin that will remain maintainable as features such as bidding, escrow integration, and website transfers are added.

Lesson 11: Creating Your First Auction from the WordPress Dashboard

Introduction

In the previous lesson, we created the first administration dashboard for Flipnzee Auctions. Although the dashboard displays useful plugin information, administrators still cannot create auctions.

In this lesson, we’ll build our first administration form.

Instead of inserting records directly into the database, administrators will be able to create auctions using the familiar WordPress Dashboard.

This marks an important milestone in the project because it connects our user interface to the database through the Auction Manager class.


Learning Objectives

By the end of this lesson, you’ll be able to:

  • Create your first administration form.
  • Submit form data securely.
  • Insert auction records into the database.
  • Use the Auction Manager class from the administration interface.
  • Understand why business logic should remain separate from the user interface.

Our Architecture

Remember the design we’ve adopted throughout this project.

Administrator

        │

        ▼

Admin Form

        │

        ▼

Auction Manager

        │

        ▼

Database

Notice that the form never communicates directly with the database.

Instead, it delegates the work to the Auction Manager.


Why Not Use SQL Here?

Imagine writing SQL inside every administration page.

Soon your project would become difficult to maintain.

Instead:

  • Forms collect information.
  • Auction Manager performs the work.
  • Database stores the results.

Each component has one responsibility.


Step 1 – Create a New Menu

Open:

admin/class-admin.php

Inside the register_menu() method, immediately below add_menu_page(), add:

add_submenu_page(
	'flipnzee-auctions',
	'Add Auction',
	'Add Auction',
	'manage_options',
	'flipnzee-add-auction',
	array( $this, 'add_auction_page' )
);

Step 2 – Create the Add Auction Page

Inside the class, add the following method.

public function add_auction_page() {

	?>

	<div class="wrap">

		<h1>Add Auction</h1>

		<form method="post">

			<table class="form-table">

				<tr>

					<th>Listing ID</th>

					<td>

						<input
							type="number"
							name="listing_id"
							min="1"
							required
						>

					</td>

				</tr>

				<tr>

					<th>Start Price</th>

					<td>

						<input
							type="number"
							step="0.01"
							name="start_price"
							required
						>

					</td>

				</tr>

				<tr>

					<th>Reserve Price</th>

					<td>

						<input
							type="number"
							step="0.01"
							name="reserve_price"
						>

					</td>

				</tr>

				<tr>

					<th>Buy Now Price</th>

					<td>

						<input
							type="number"
							step="0.01"
							name="buy_now_price"
						>

					</td>

				</tr>

			</table>

			<?php submit_button( 'Create Auction' ); ?>

		</form>

	</div>

	<?php
}

Step 3 – Why Listing ID?

Some readers may wonder why we’re asking for a Listing ID instead of a website title.

The answer is simple.

Website listings already belong to the Flipnzee Analytics plugin.

Rather than storing duplicate information, Flipnzee Auctions simply references the existing listing.

For now, we’ll enter the Listing ID manually.

Later in this series, we’ll replace this field with a dropdown that automatically displays verified listings from Flipnzee Analytics.


Step 4 – Upload the Plugin

Create a new ZIP.

Upload it.

Activate the plugin.

Open:

Flipnzee Auctions

↓

Add Auction

You should now see your first administration form.

Although the form doesn’t yet save data, we’ve successfully built the user interface.

We’ll connect it to the Auction Manager in the next lesson.


Lesson Summary

In this lesson, we created our first administration form for Flipnzee Auctions.

Rather than inserting data directly into the database, we focused on building a clean user interface that will soon communicate with the Auction Manager.

This layered approach keeps the plugin organized and prepares us for more advanced functionality.


Key Takeaways

  • ✓ WordPress administration pages use standard HTML forms.
  • ✓ Keep forms separate from database logic.
  • ✓ Prepare for future integrations instead of duplicating data.
  • ✓ Build one layer at a time.

Common Mistakes

  • Writing SQL directly inside administration pages.
  • Mixing business logic with HTML.
  • Duplicating listing information.
  • Building complicated forms before the backend is ready.

Git Commands Used

git add .

git commit -m "Lesson 11: Add Auction form"

git push

Project Status

✅ Development environment

✅ Plugin skeleton

✅ Plugin installation

✅ Plugin lifecycle

✅ .gitignore

✅ Data architecture

✅ Database table

✅ Auction Manager

✅ Retrieve auction records

✅ WordPress admin menu

✅ Plugin dashboard

✅ Add Auction page

⬜ Save auction

⬜ List auctions

⬜ Edit auctions

⬜ Bid engine

⬜ Escrow workflow

⬜ Website transfer

⬜ Version 1.0

Project Evolution

When we first planned this plugin, we considered storing all website information inside Flipnzee Auctions.

As the project evolved, we recognized that Flipnzee Analytics already provides verified website listings. Rather than duplicating that data, the auction plugin now references listings through a listing_id.

This architectural refinement keeps both plugins modular and easier to maintain while allowing them to work together as part of the broader Flipnzee ecosystem.


Developer’s Notebook

One of the biggest advantages of separating the user interface from the business logic is flexibility. Today we’re creating a simple administration form, but tomorrow the same Auction Manager methods could be called from REST APIs, AJAX requests, WP-CLI commands, or even another plugin. By keeping responsibilities separate, we avoid rewriting the core logic every time a new interface is introduced.


Looking Ahead

In Lesson 12, we’ll connect this form to the Auction Manager so that submitting it actually creates a new auction in the database. We’ll also introduce WordPress nonces to protect the form against unauthorized submissions, an essential security practice for professional plugin development.