Database Schema
The Pro version of the ID Payment Link Generator creates three custom database tables to store customers, email logs, and email templates. This page documents the full schema, including column types, indexes, and the CRUD classes you can use to interact with the data programmatically.
Pro Feature: Custom database tables are only created when the Pro version is activated. The free version stores only its settings in the
idplg_settingsoption and does not create any custom tables.
Table Creation & Versioning
Tables are created (or updated) during plugin activation via the IDPLG_Database::install() method, which uses WordPress’s dbDelta() function. This ensures safe, incremental schema changes — new columns can be added without losing existing data.
The current schema version is tracked in the idplg_db_version option. On each activation, the plugin compares the stored version against the code’s version and runs dbDelta() if an update is needed.
Table: {prefix}idplg_customers
Stores customer contact information for quick selection when generating payment links.
| Column | Type | Attributes | Description |
|---|---|---|---|
id |
bigint(20) unsigned |
PRIMARY KEY, AUTO_INCREMENT | Unique customer identifier |
first_name |
varchar(100) |
NOT NULL, DEFAULT ” | Customer’s first name |
last_name |
varchar(100) |
NOT NULL, DEFAULT ” | Customer’s last name |
email |
varchar(200) |
NOT NULL, DEFAULT ”, INDEXED | Customer’s email address |
company |
varchar(200) |
NOT NULL, DEFAULT ” | Company or organization name |
phone |
varchar(50) |
NOT NULL, DEFAULT ” | Phone number |
notes |
text |
Internal notes about the customer | |
created_at |
datetime |
NOT NULL | Record creation timestamp |
updated_at |
datetime |
NOT NULL | Last update timestamp |
Indexes:
PRIMARY KEY (id)KEY email (email)— Speeds up customer search and duplicate detection.
Table: {prefix}idplg_log
Records every email send attempt, including both successful and failed deliveries. Stores a snapshot of the data at the time of sending rather than referencing the customer table, so log entries remain accurate even if customer details are later changed.
| Column | Type | Attributes | Description |
|---|---|---|---|
id |
bigint(20) unsigned |
PRIMARY KEY, AUTO_INCREMENT | Unique log entry identifier |
customer_id |
bigint(20) unsigned |
NULLABLE, INDEXED | Associated customer ID (NULL if not linked) |
first_name |
varchar(100) |
NOT NULL, DEFAULT ” | Recipient’s first name (snapshot) |
last_name |
varchar(100) |
NOT NULL, DEFAULT ” | Recipient’s last name (snapshot) |
email |
varchar(200) |
NOT NULL, DEFAULT ” | Recipient’s email address (snapshot) |
amount |
varchar(50) |
NOT NULL, DEFAULT ” | Payment amount (as entered by admin) |
payment_link |
text |
The full generated payment link URL | |
email_subject |
varchar(500) |
NOT NULL, DEFAULT ” | The email subject line used |
status |
varchar(20) |
NOT NULL, DEFAULT ‘sent’, INDEXED | Delivery status: 'sent' or 'failed' |
created_at |
datetime |
NOT NULL, INDEXED | Timestamp of the send attempt |
Indexes:
PRIMARY KEY (id)KEY customer_id (customer_id)— Enables efficient lookup of all emails sent to a specific customer.KEY status (status)— Supports filtering by sent/failed status.KEY created_at (created_at)— Supports date range queries and chronological sorting.
Table: {prefix}idplg_email_templates
Stores reusable email templates with placeholder support. One template can be marked as the default, which is automatically selected on the generator page.
| Column | Type | Attributes | Description |
|---|---|---|---|
id |
bigint(20) unsigned |
PRIMARY KEY, AUTO_INCREMENT | Unique template identifier |
name |
varchar(200) |
NOT NULL, DEFAULT ” | Template display name |
subject |
varchar(500) |
NOT NULL, DEFAULT ” | Email subject line (supports placeholders) |
body |
longtext |
Email body HTML (supports placeholders) | |
is_default |
tinyint(1) |
NOT NULL, DEFAULT 0, INDEXED | Whether this is the default template (1 = yes) |
created_at |
datetime |
NOT NULL | Record creation timestamp |
updated_at |
datetime |
NOT NULL | Last update timestamp |
Indexes:
PRIMARY KEY (id)KEY is_default (is_default)— Quick lookup of the default template.
CRUD Classes
The plugin provides three static CRUD classes for interacting with the database tables. All classes use $wpdb with prepared statements to prevent SQL injection.
IDPLG_Customers
Handles all customer data operations.
IDPLG_Customers::get( $id )— Returns a single customer object ornull.IDPLG_Customers::search( $term )— Searches customers by name, email, or company. Returns array of objects.IDPLG_Customers::save( $data )— Inserts (if$data['id']is 0) or updates a customer. Returns the customer ID.IDPLG_Customers::delete( $id )— Deletes a customer by ID. Returnstrueon success.IDPLG_Customers::get_all( $args )— Retrieves customers with pagination, sorting, and search. Returns array.IDPLG_Customers::count( $search )— Returns total customer count, optionally filtered by search term.
IDPLG_Log
Handles email log operations.
IDPLG_Log::add( $data )— Inserts a new log entry. Returns the log ID.IDPLG_Log::get( $id )— Returns a single log entry object ornull.IDPLG_Log::get_all( $args )— Retrieves log entries with filtering, pagination, and sorting.IDPLG_Log::count( $args )— Returns total log count with optional filters.IDPLG_Log::get_for_customer( $customer_id )— Returns all log entries for a specific customer.
IDPLG_Email_Templates
Handles email template operations.
IDPLG_Email_Templates::get( $id )— Returns a single template object ornull.IDPLG_Email_Templates::get_default()— Returns the default template object.IDPLG_Email_Templates::get_all()— Returns all templates, ordered by name.IDPLG_Email_Templates::save( $data )— Inserts or updates a template. Returns the template ID.IDPLG_Email_Templates::delete( $id )— Deletes a template by ID. Returnstrueon success.
Direct Database Query Example
While using the CRUD classes is recommended, you can also query the tables directly with $wpdb for advanced use cases:
<?php
/**
* Example: Query customers directly using $wpdb.
*
* This demonstrates how to perform a custom query against
* the idplg_customers table with proper prepared statements.
*/
global $wpdb;
$table_name = $wpdb->prefix . 'idplg_customers';
// Get all customers with a specific email domain.
$domain = 'example.com';
$customers = $wpdb->get_results(
$wpdb->prepare(
"SELECT id, first_name, last_name, email, company
FROM {$table_name}
WHERE email LIKE %s
ORDER BY last_name ASC, first_name ASC",
'%@' . $wpdb->esc_like( $domain )
)
);
if ( $customers ) {
foreach ( $customers as $customer ) {
printf(
"Customer #%d: %s %s (%s) - %s\n",
$customer->id,
$customer->first_name,
$customer->last_name,
$customer->email,
$customer->company ?: 'No company'
);
}
} else {
echo "No customers found for domain: {$domain}\n";
}
/**
* Example: Get email send statistics for the last 30 days.
*/
$log_table = $wpdb->prefix . 'idplg_log';
$stats = $wpdb->get_row(
$wpdb->prepare(
"SELECT
COUNT(*) as total,
SUM( CASE WHEN status = 'sent' THEN 1 ELSE 0 END ) as sent,
SUM( CASE WHEN status = 'failed' THEN 1 ELSE 0 END ) as failed
FROM {$log_table}
WHERE created_at >= %s",
gmdate( 'Y-m-d H:i:s', strtotime( '-30 days' ) )
)
);
printf(
"Last 30 days: %d total emails, %d sent, %d failed (%.1f%% success rate)\n",
$stats->total,
$stats->sent,
$stats->failed,
$stats->total > 0 ? ( $stats->sent / $stats->total * 100 ) : 0
);
/**
* Example: Find customers who haven't been emailed in 90 days.
*/
$inactive_customers = $wpdb->get_results(
$wpdb->prepare(
"SELECT c.id, c.first_name, c.last_name, c.email,
MAX(l.created_at) as last_email_date
FROM {$table_name} c
LEFT JOIN {$log_table} l ON c.id = l.customer_id
GROUP BY c.id
HAVING last_email_date IS NULL
OR last_email_date < %s
ORDER BY last_email_date ASC",
gmdate( 'Y-m-d H:i:s', strtotime( '-90 days' ) )
)
);
foreach ( $inactive_customers as $customer ) {
printf(
"%s %s (%s) - Last emailed: %s\n",
$customer->first_name,
$customer->last_name,
$customer->email,
$customer->last_email_date ?: 'Never'
);
}
Important: Always use
$wpdb->prepare()for any queries that include user input. Never concatenate variables directly into SQL strings. The CRUD classes handle this automatically, but direct queries require manual preparation.
Table Prefix Note
Always use $wpdb->prefix to get the correct table prefix. This is especially important on WordPress multisite installations where each site has a unique prefix (e.g., wp_2_idplg_customers). Never hardcode wp_ as the prefix.
