# XShop – Database Model ## 1. Philosophy Reuse WP/WC primitives. No custom tables in v1.0 – revisit via ADR if query scale warrants. Document every key. ## 2. WordPress-Owned - **Users** (`wp_users`, `wp_usermeta`): native. XShop adds: `xshop_wishlist` (array of product IDs), `xshop_compare` (array), `xshop_recently_viewed` (capped list). - **Posts/Pages** (`wp_posts`, `wp_postmeta`): native. - **Media** (`wp_posts` attachment): native. - **Menus** (`wp_terms` nav_menu): native. - **Comments/Reviews**: WC reviews = WP comments on `product` post type. XShop Q&A moderation hooks onto comment moderation flow where applicable – but primary store is custom (below). ## 3. WooCommerce-Owned - **Products** (`wp_posts` post_type `product` + `wp_postmeta` + `wp_wc_*` where HPOS enabled). Variations are `product_variation` children. - **Orders/Customers/Coupons/Shipping/Payment/Taxes/Cart/Checkout**: WC owned (HPOS tables if enabled). XShop never writes directly to WC order tables except via WC APIs. ## 4. XShop-Core Custom Data ### 4.1 Banners – CPT `xshop_banner` - `post_type = xshop_banner` (non-public, `show_ui` true under XShop menu). - Meta: - `_xshop_banner_image` (attachment ID, int) - `_xshop_banner_mobile_image` (attachment ID, int, nullable) - `_xshop_banner_link` (url, esc_url_raw) - `_xshop_banner_cta` (string) - `_xshop_banner_position` (enum: `home_hero`, `home_strip`, `shop_top`, `product_sidebar`, etc.) - `_xshop_banner_active` (`1`|`0`) - `_xshop_banner_start` / `_xshop_banner_end` (datetime Y-m-d H:i:s, UTC) - `_xshop_banner_order` (int) - Scheduling checked on render (skip if outside window or inactive). ### 4.2 Questions & Answers – CPT `xshop_qa` Decision: CPT (not comments) for cleaner moderation + threading without polluting reviews. - `post_type = xshop_qa`, `post_parent` = product ID (or meta `_xshop_qa_product_id` for query flexibility – both stored, parent is canonical). - Post fields: `post_title` empty, `post_content` = question body, `post_status` = `pending`|`publish`|`trash`, `post_author` = asker. - Meta: - `_xshop_qa_product_id` (int, indexed via meta query) - `_xshop_qa_answer` (string, nullable – single answer for v1; extension to multiple answers uses child `xshop_qa` rows with `post_parent` = question ID in v1.1) - `_xshop_qa_answered_by` (int user ID) - `_xshop_qa_answered_at` (datetime) - Alternative for multiple answers (future): child posts `post_type=xshop_qa`, `post_parent=question ID`. - Spam: honeypot + nonce + rate limit (transient per IP/user, 60s). ### 4.3 Wishlist & Compare - **Logged-in**: `wp_usermeta` keys `xshop_wishlist`, `xshop_compare` (serialized array of ints, capped: wishlist 200, compare 4). - **Guest**: cookie `xshop_wishlist` / `xshop_compare` (JSON, HttpOnly false for JS count, SameSite Lax, 30 days) + optional transient `xshop_guest_`. - On login: merge cookie → user meta (union, cap), clear cookie. - Counts exposed via REST and localized script data. ### 4.4 Recently Viewed - Cookie `xshop_recently_viewed` (JSON array of product IDs, capped 20, 30 days). No DB write for guests. Logged-in also stored in `xshop_recently_viewed` user meta for cross-device. ## 5. Options - Single option `xshop_settings` (array) – all Theme Options. Autoload `yes`. Individual mods also mirrored via `theme_mod` for Customizer preview where needed, but source of truth is `xshop_settings`. - Option `xshop_settings_backup` (for import/export). - Transient `xshop_search_cache_` (5 min) for AJAX search results. - Transient `xshop_filter_counts_` (5 min) for filter counts. Do NOT scatter random options. ## 6. Indexes & Query Patterns - Banners: query by `post_type` + meta `position`+`active`+date range → meta query; small cardinality. - Q&A: `WP_Query` by `post_type=xshop_qa` + `post_parent=product_id` + `post_status=publish` (index on `post_parent` + `post_type` already exists). - Wishlist/Compare: user meta single-row lookup – O(1). - Search: `WP_Query` with `s` + `post_type=product` + `tax_query` for category, `meta_query` for SKU (`_sku`), limited to 8 suggestions, `no_found_rows` true, `fields=ids` then prime caches. ## 7. Migrations & Compatibility - HPOS: use `wc_get_products()` / CRUD, not direct `wp_postmeta` writes for product data. - On plugin deactivate: data retained (no destructive cleanup). Uninstall hook offered to purge (`uninstall.php` with confirmation).