Part 8 of 10 · MaidsMe Pro

The Database, and the Tables I Deleted When I Changed Direction

By the second redesign, I had stopped thinking of the plugin as code with a database attached, and started seeing it truthfully, MaidsMe is a database with screens attached. This post is that schema as it ended up, the versioned migrations that let it change safely, and the tables I deleted, with the export-first habit that made deleting survivable.

The final shape is seven tables, each owning one concern:

// mm_professions   categories: maid, nanny, driver, cook, caregiver...
// mm_fields        field definitions (type, label, required, options)
// mm_field_map     which fields belong to which profession, visibility flags
// mm_profiles      the worker: identity, status, encrypted phone, timestamps
// mm_profile_values answers as rows: profile_id | field_id | value
// mm_inquiries     anonymous Hire Now submissions with a status queue
// mm_status_log    every profile status change: who, what, when

Creation runs through dbDelta at activation, with a schema version so upgrades are migrations, not accidents:

private function migrate() {
    $installed = get_option('mm_db_version', '0');

    if (version_compare($installed, '2.0', '<')) {
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($this->schema_sql());          // creates / updates tables
        update_option('mm_db_version', '2.0');
    }
}

dbDelta is WordPress’s schema tool, hand it CREATE TABLE statements and it creates missing tables and adds missing columns without touching data, non-destructive by design, though it demands fussy SQL formatting, two spaces after PRIMARY KEY, keys named explicitly, quirks that cost an evening once and never again. The version option is the part people skip and regret, comparing an installed version against the code’s version turns every future change into an ordered migration instead of a guess about what state this site is in.

And the deletions. Version one’s employers, messages, and sessions tables served the account-and-chat design the teardown removed. Dead tables are not harmless, they invite dead code to keep querying them, so they had to go, but deletion got a discipline, export first, drop second:

// before any DROP: a dated JSON export of the doomed tables
$rows = $wpdb->get_results('SELECT * FROM ' . $old_table, ARRAY_A);
file_put_contents(
    WP_CONTENT_DIR . '/mm-backup-' . $old_table . '-' . gmdate('Ymd') . '.json',
    wp_json_encode($rows)
);
$wpdb->query('DROP TABLE IF EXISTS ' . $old_table);

Cheap insurance, a few kilobytes of JSON, and the freedom to delete decisively because deletion stopped being irreversible. Nothing from those exports was ever needed. That is what good insurance looks like.

A few things people ask me about this

Why does dbDelta ignore my table changes? Formatting. It parses your SQL with strict expectations, each field on its own line, PRIMARY KEY followed by two spaces, KEY names explicit. Match the documented format exactly and it behaves.

Where should plugin table creation run? In the activation hook, through a migration function guarded by a version option, so reactivation and updates are safe no-ops when nothing changed.

Next

With the machinery sound, the surface had to earn something machinery cannot, trust at first sight. Why a trust platform lives or dies on how it looks is the next post.

Leave a Reply

Your email address will not be published. Required fields are marked *