{prefix}lh_poll_options
Per-site on Multisite.
| Column | Type | Description |
|---|---|---|
| option_id | BIGINT UNSIGNED | Primary key |
| poll_id | BIGINT UNSIGNED | Parent poll post ID |
| label | VARCHAR(255) | Display label |
| menu_order | INT | Sort order |
{prefix}lh_poll_votes
Per-site on Multisite.
| Column | Type | Description |
|---|---|---|
| vote_id | BIGINT UNSIGNED | Primary key |
| poll_id | BIGINT UNSIGNED | Parent poll post ID |
| option_id | BIGINT UNSIGNED | Option voted for |
| user_id | BIGINT UNSIGNED | Voter |
| comment_id | BIGINT UNSIGNED | Linked wp_comments row |
| voted_at | DATETIME | Vote timestamp |
Unique constraint: (option_id, user_id) – one vote per user per option, enforced at the database level.
wp_comments (comment_type = lh_vote)
Every vote creates a wp_comments row of type lh_vote attached to the poll post (comment_post_ID = poll_id). This enables optional comment text alongside a vote, viewing/deleting votes via the standard WP comments admin screen, and vote counts visible as comment_count on the poll post. Vote comments are excluded from all standard comment queries, feeds, and counts on non-poll posts. Deleting a vote comment via the WP comments screen automatically removes the corresponding vote row via the deleted_comment hook. post_parent on lh_poll is unused (default 0) – not repurposed for anything.
Caching
All poll read queries (options list, option IDs, vote counts, per-option vote list, per-user voted flags) are cached as transients backed by the persistent object cache (Redis on LocalHero). Cache is keyed per poll/option/user and flushed explicitly on any vote insert, delete, or option change via LH_Polls_DB::flush_poll_cache() – TTL (DAY_IN_SECONDS) is a safety net only, since invalidation is explicit on every write.
Cache invalidation (page cache)
After a vote is cast or changed, the plugin fires do_action(‘litespeed_purge_url’, $page_url) to purge the embedding page from LiteSpeed’s page cache. A no-op on non-LiteSpeed environments.