Database Schema (User Meta Fields)
All fields are stored as user meta (wp_usermeta), under the plugin’s namespace prefix lh_profile-. There is no dedicated database table.
Registered meta (validated via register_meta())
These fields have a sanitize_callback registered, so any direct update_user_meta() write β from this plugin, the REST API, or any other plugin β is validated/normalised, not just writes that go through this plugin’s own save paths.
| Meta key | Type | Format | Notes |
|---|---|---|---|
lh_profile-phone |
string | E.164 (+61...) |
Validated via giggsey/libphonenumber-for-php-lite (Composer). A full backfill (this session) converted ~8,300 legacy-format rows to E.164. |
lh_profile-secondary_phone |
string | E.164 | Same validator as phone. |
lh_profile-gender |
string |
Male|Female|Organisation|Robot
|
Fixed allowed-value set. |
lh_profile-birthdate |
string | Y-m-d |
Changed from a UNIX timestamp to a plain date string in v2.13 β see Architectural Notes for why, and the full migration history. |
lh_profile-street_address |
string | free text |
sanitize_text_field() only β no richer format exists. |
lh_profile-town |
string | free text |
sanitize_text_field() only. |
lh_profile-postcode |
string | free text |
sanitize_text_field() only for now β real per-country format validation (AU 4-digit, US ZIP, UK alphanumeric) is a known future candidate, not yet built. |
lh_profile-state_province |
string | free text |
sanitize_text_field() only. |
lh_profile-country |
string | free text |
sanitize_text_field() only β a plain text field, not a country-code dropdown. |
Unregistered meta
| Meta key | Type | Notes |
|---|---|---|
lh_profile-birthdate_readable |
string | Companion field alongside lh_profile-birthdate, written by handle_birthdate_update(). Historically held the pre-parse date string; since the v2.13 format change both fields hold the same Y-m-d value, making this field largely redundant going forward, but it’s kept for backward compatibility since other code (filter_query_joins() for the “Users Insights” plugin integration) reads it directly. |
lh_profile-emails |
array | Additional/secondary emails, serialized array of validated email strings. Slated for removal β superseded by lh-user-identity. Not extracted into its own field class for this reason. As of this session, 1,888 users have a non-empty value here, largely following a firstname.lastname@theaustraliatimes.com.au <-> real-email cross-reference pattern that turned out to indicate a large number of duplicate user accounts β see the exploration brief referenced in Architectural Notes. |
lh_profile-updated_time |
int | UNIX timestamp of the last time any “non-native” field (see below) was updated. Set by track_last_meta_update_time(). |
lh_profile-updated_field |
string | Which meta key was last updated, alongside updated_time. |
Core WordPress fields (not this plugin’s own meta)
| Field | Storage | Notes |
|---|---|---|
| First name |
first_name usermeta (WP core key) |
No register_meta() here deliberately β registering a sanitize_callback against a core-recognised key would affect any other code touching that exact key, not just this plugin. |
| Last name |
last_name usermeta (WP core key) |
Same reasoning as first name. |
| Display name |
wp_users.display_name column |
Not usermeta at all β register_meta() doesn’t apply. |
“Non-native fields” list
return_all_non_native_fields() returns: street_address, town, postcode, gender, phone, birthdate (all namespace-prefixed). This list drives several cross-cutting behaviours: vcard export tracking, notification token generation, the “updated_time”/”updated_field” tracking, and the prevent_bad_meta_values filter (rejects empty-string writes to any of these keys).
