Lesson 6: Creating Your First Database Table with dbDelta()

Introduction

In the previous lesson, we made one of the most important architectural decisions for the Flipnzee ecosystem.

Rather than building one massive plugin that manages everything, we decided to split responsibilities between two plugins:

  • Flipnzee Analytics manages website listings, verification, Google Analytics, Search Console data, and marketplace insights.
  • Flipnzee Auctions manages auctions, bidding, transactions, and the website transfer process.

This separation of responsibilities keeps both plugins modular, easier to maintain, and easier to extend in the future.

Now it’s time to create our first custom database table.

WordPress provides a special function called dbDelta() that safely creates or updates database tables. Instead of executing raw SQL directly, dbDelta() compares your desired table structure with the existing database and applies only the necessary changes.

In this lesson, we’ll use dbDelta() to create the first table for the Flipnzee Auctions plugin.


Learning Objectives

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

  • Understand why WordPress provides the dbDelta() function.
  • Create a custom database table during plugin activation.
  • Use the $wpdb object correctly.
  • Understand the importance of $wpdb->prefix.
  • Design a database table that complements another plugin instead of duplicating data.

Why Are We Creating a Custom Table?

Earlier in this series, we decided that auctions are transactional data, not content.

A website listing already exists in Flipnzee Analytics.

The auction plugin only needs to know:

  • Which listing is being auctioned.
  • The auction settings.
  • The current bid.
  • The auction status.
  • Who won the auction.

This keeps each plugin focused on its own responsibility.


Our Plugin Architecture

Our project now looks like this:

Flipnzee Analytics
--------------------------
Website Listings
Google Analytics
Search Console
Traffic
Revenue
Verification

          │
          ▼

Flipnzee Auctions
--------------------------
Auction
Bids
Escrow Workflow
Transfers
Watchlists
Notifications

Notice that the Auctions plugin does not store website information.

Instead, it references an existing listing.


Why Not Duplicate Website Data?

Suppose we copied the following into the auction table:

  • Website title
  • Monthly traffic
  • Revenue
  • Domain age

Now imagine the seller updates the listing.

Which copy should the buyer trust?

Instead of maintaining two copies of the same information, our auction simply stores a reference to the listing managed by Flipnzee Analytics.

This is a common software engineering principle known as avoiding duplication.


Step 1 – Create the Database Class

Inside the includes folder, create a new file:

class-database.php

Step 2 – Add the Database Class

<?php

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

class Flipnzee_Auction_Database {

	public static function create_tables() {

		global $wpdb;

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

		$charset_collate = $wpdb->get_charset_collate();

		require_once ABSPATH . 'wp-admin/includes/upgrade.php';

		$sql = "CREATE TABLE {$table_name} (

			id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,

			listing_id BIGINT UNSIGNED NOT NULL,

			status VARCHAR(30) DEFAULT 'draft',

			start_price DECIMAL(12,2) DEFAULT 0,

			reserve_price DECIMAL(12,2) DEFAULT 0,

			buy_now_price DECIMAL(12,2) DEFAULT 0,

			current_bid DECIMAL(12,2) DEFAULT 0,

			auction_start DATETIME NULL,

			auction_end DATETIME NULL,

			winner_user_id BIGINT UNSIGNED DEFAULT NULL,

			created_at DATETIME DEFAULT CURRENT_TIMESTAMP,

			updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

			PRIMARY KEY (id),

			KEY listing_id (listing_id),

			KEY status (status)

		) {$charset_collate};";

		dbDelta( $sql );

	}
}

Understanding Each Field

id

The unique identifier for each auction.


listing_id

This is the most important field in our table.

Instead of storing website information again, we simply reference the existing listing managed by Flipnzee Analytics.

One listing may eventually have multiple auctions over its lifetime without duplicating data.


status

Examples include:

  • draft
  • scheduled
  • active
  • ended
  • sold
  • cancelled

start_price

The opening bid.


reserve_price

The seller’s minimum acceptable price.


buy_now_price

Allows a buyer to immediately purchase the website without waiting for the auction to end.


current_bid

Stores the highest bid.

Although individual bids will later have their own database table, storing the current highest bid here allows auction listings to load much faster.


auction_start

The date and time bidding begins.


auction_end

The date and time bidding closes.


winner_user_id

After the auction ends, this field stores the winning bidder.


created_at and updated_at

These timestamps help us monitor when auction records are created and modified.


Step 3 – Load the Database Class

Open:

flipnzee-auctions.php

Add:

require_once FLIPNZEE_AUCTION_PATH . 'includes/class-database.php';

Step 4 – Update the Activation Hook

Replace your activation function with:

function flipnzee_auction_activate()
{
	Flipnzee_Auction_Database::create_tables();

	flush_rewrite_rules();
}

Now, whenever the plugin is activated, WordPress checks whether the auction table exists.

If it doesn’t, dbDelta() creates it automatically.


Step 5 – Test Your Plugin

Deactivate the plugin.

Activate it again.

If everything has been configured correctly, the database table will be created without displaying any errors.


Step 6 – Verify the Table

Open your preferred database manager, such as:

  • phpMyAdmin
  • Adminer
  • MySQL Workbench

You should now see:

wp_flipnzee_auctions

If your WordPress installation uses a different table prefix, the table name will begin with that prefix instead of wp_.


Why We’re Keeping This Table Simple

You might notice that this table doesn’t include:

  • Bid history
  • Payments
  • Escrow transactions
  • Website transfers

That’s intentional.

Each of these responsibilities will receive its own dedicated database table in future lessons.

Keeping each table focused on a single responsibility makes the database easier to maintain and allows the plugin to scale as more features are added.


Lesson Summary

In this lesson, we created our first custom database table using WordPress’s dbDelta() function. Instead of duplicating website information, we designed the table to reference listings managed by Flipnzee Analytics using the listing_id field.

This modular architecture allows Flipnzee Analytics to remain the source of truth for website information while Flipnzee Auctions focuses entirely on managing auctions and transactions.


Key Takeaways

  • ✓ Use dbDelta() when creating WordPress database tables.
  • ✓ Always use $wpdb->prefix.
  • ✓ Avoid duplicating data between plugins.
  • ✓ Design each database table with a single responsibility.
  • ✓ Modular plugins are easier to maintain and extend.

Common Mistakes

  • Hardcoding the wp_ table prefix.
  • Forgetting to load upgrade.php.
  • Storing the same information in multiple tables.
  • Mixing listing data with auction data.
  • Forgetting to reactivate the plugin after updating the activation hook.

Git Commands Used

git add .

git commit -m "Lesson 6: Create auction database table"

git push

Project Evolution

If you’ve been following this series from Lesson 0, you may notice that the architecture of Flipnzee Auctions has evolved as the project has grown.

Originally, the plugin was planned as a standalone website auction platform responsible for both listings and auctions.

As development progressed, we realized that Flipnzee Analytics already provides website listings, verification, Google Analytics integration, and Search Console data. Rather than duplicating those responsibilities, we decided to keep Flipnzee Auctions focused on auctions and transaction management.

This architectural change brings several advantages:

  • It avoids storing the same data in multiple places.
  • It keeps each plugin focused on a single responsibility.
  • It makes both plugins easier to maintain.
  • It allows future improvements to Flipnzee Analytics to benefit the auction system automatically.

You’ll notice similar design refinements throughout this series. That’s intentional. Real-world software development is an iterative process, and one of the goals of this series is to demonstrate how professional software evolves over time rather than presenting it as if it were designed perfectly from the beginning.

The GitHub repository will always contain the latest implementation, while these lessons document the reasoning behind each important decision.

Project Status

✅ Development environment

✅ Plugin skeleton

✅ Plugin installation

✅ Plugin lifecycle

✅ .gitignore

✅ Data architecture

✅ First database table

⬜ Auction Manager

⬜ Bid engine

⬜ Escrow workflow

⬜ Website transfer workflow

⬜ Notifications

⬜ Version 1.0

Developer’s Notebook

One of the most important principles in software engineering is Separation of Concerns. Each component of a system should have a clearly defined responsibility.

In the Flipnzee ecosystem, Flipnzee Analytics is responsible for collecting and presenting website information, while Flipnzee Auctions is responsible for managing the buying and selling process. By keeping these responsibilities separate, we avoid duplication, reduce complexity, and make both plugins easier to maintain as they evolve.


Looking Ahead

In Lesson 7, we’ll create the Auction Manager class and learn how to insert, retrieve, update, and delete auction records using WordPress’s $wpdb methods. This will be the first time our plugin stores and retrieves real auction data, moving us from database design into application development.

Leave a Reply