Skip to content

Database Schema ​

WP Social Ninja stores its data in a small set of custom tables (created by the core plugin's migration runner) combined with WordPress's own posts, postmeta, and options tables. All custom tables use your site's WordPress table prefix — shown here as wpsr_. Pro and Custom Feed for TikTok add no tables of their own; they read and write these core tables.

Entity Overview ​

🔍 Interactive diagram

Click the diagram to zoom in. It shows the core tables and how templates/widgets map onto WordPress posts / postmeta.

Schema Overview ​

The WP Social Ninja schema is built around a few core concepts:

  • Reviews & Testimonials: a single wpsr_reviews table holds every review, testimonial, and custom/form-collected review, keyed by platform and source.
  • Caching: wpsr_caches stores remote API/feed responses with an expiration and a failure counter for back-off.
  • Review Forms: wpsr_review_forms stores native and Pro review-collection form definitions (schema + settings).
  • Categories 4.4.0: wpsr_review_categories plus a pivot table give reviews and testimonials a shared, many-to-many vocabulary, replacing the single wpsr_reviews.category column.
  • Image Optimization: wpsr_optimize_images tracks locally optimized media.
  • Templates & Widgets: stored as WordPress posts with _wpsr_* post meta.
  • Settings: stored as WordPress options (prefixed wpsr_, autoload disabled).

Key Design Principles ​

  1. Single reviews table — all review-type content lives in wpsr_reviews, distinguished by platform_name and recommendation_type rather than separate per-platform tables.
  2. JSON overflow column — fields (LONGTEXT, cast to json on the model) holds platform-specific data that doesn't warrant its own column.
  3. Strategic indexing — frequently filtered columns (platform_name, source_id, review_id, review_approved, reviewer_email) are indexed, plus composite indexes on (review_time, id) and (rating, review_time, id) for the sorted listings.
  4. WordPress-native where it fits — templates/widgets reuse posts/postmeta; settings reuse options, so no bespoke tables are needed for them.
  5. Incremental, idempotent migrations — migrators create-or-alter, adding missing columns and indexes on upgrade; bump WPSOCIALREVIEWS_DB_VERSION when the schema changes.
  6. Application-level uniqueness — e.g. one-review-per-email is enforced in PHP (respecting the one_review_per_email toggle), with reviewer_email indexed for fast lookups.

Database Tables ​

wpsr_reviews Table ​

Stores all reviews, testimonials, and custom reviews. Migrator: ReviewsMigrator.php.

ColumnTypeComment
idint(11) NOT NULL Auto IncrementPrimary key
platform_namevarchar(255)Platform slug (google, facebook, custom, …)
source_idvarchar(255)Connected source / form ID
review_idvarchar(255)Remote review ID
categoryvarchar(255)Legacy single category — superseded in 4.4.0, see below
review_titlevarchar(255)Review title
reviewer_namevarchar(255)Reviewer display name
reviewer_emailvarchar(255) DEFAULT NULLReviewer email
reviewer_urlvarchar(255)Reviewer profile URL
reviewer_imgTEXT NULLReviewer avatar URL
reviewer_textLONGTEXT NULLReview body
review_timetimestamp NULLReview timestamp
ratingint(11)Star rating
review_approvedint(11) DEFAULT 1Approval flag
recommendation_typevarchar(255)e.g. Facebook recommendation type
fieldsLONGTEXT NULLJSON blob of extra fields (model cast: json)
created_atTIMESTAMP NULL
updated_atTIMESTAMP NULL

Indexes:

IndexColumns
idx_platform_nameplatform_name
idx_review_idreview_id
idx_source_idsource_id
idx_review_approvedreview_approved
idx_reviewer_emailreviewer_email
idx_review_time 4.4.0review_time, id
idx_rating_time 4.4.0rating, review_time, id

idx_rating was dropped in 4.4.0

idx_rating_time leads on rating, so it serves everything the single-column index did. The migration drops idx_rating only after confirming the replacement is really present, so a failed ALTER leaves the old index in place.

Both sort indexes end in id to match the query's tie-break. If you issue your own queries against wpsr_reviews with an explicit USE INDEX (idx_rating), switch to idx_rating_time.

wpsr_review_categories Table 4.4.0 ​

Reusable review/testimonial categories — one shared vocabulary across both screens. Migrator: ReviewCategoriesMigrator.php.

ColumnTypeComment
idbigint(20) NOT NULL Auto IncrementPrimary key
namevarchar(191) NOT NULLDisplay name
slugvarchar(191) NOT NULLDerived from the name
created_atTIMESTAMP NULL
updated_atTIMESTAMP NULL

Indexes:

IndexColumns
idx_slug (UNIQUE)slug

varchar(191) rather than 255 because slug is UNIQUE-indexed and utf8mb4 past 191 characters exceeds InnoDB's 767-byte key limit. name matches that width so two names cannot truncate to the same slug.

wpsr_review_category_relationships Table 4.4.0 ​

Pivot between reviews and categories. A review may carry several categories. Migrator: ReviewCategoriesMigrator.php.

ColumnTypeComment
idbigint(20) NOT NULL Auto IncrementPrimary key
review_idint(11) NOT NULLwpsr_reviews.id
category_idbigint(20) NOT NULLwpsr_review_categories.id

Indexes:

IndexColumns
idx_review_category (UNIQUE)review_id, category_id
idx_category_idcategory_id

review_id is int(11) to match wpsr_reviews.id, not bigint(20) like the other keys — a mismatched width would prevent the join from using the index.

No foreign keys

There are no FK constraints, so nothing at the database level stops an orphaned pivot row. The application compensates: RecommendationsController verifies a client-supplied category_id resolves to a real category before writing, and the migrator runs a bounded orphan sweep. If you write pivot rows directly, validate both ids yourself.

Backfill from the legacy category column ​

The migrator copies wpsr_reviews.category into the taxonomy in bounded batches across requests rather than in one pass, so a large install does not time out. It tracks progress in options:

OptionPurpose
wpsr_review_categories_backfilledtrue once the legacy column is fully copied
wpsr_review_categories_backfill_cursorBatch cursor between runs
wpsr_review_categories_orphan_sweep_pendingA deferred orphan sweep is outstanding

The legacy category column is retained, not dropped — the backfill reads it and skips when it is absent. Treat it as read-only for new code and go through the pivot instead.

wpsr_caches Table ​

Feed and API response cache. Migrator: CacheMigrator.php.

ColumnTypeComment
idbigint(20) NOT NULL Auto IncrementPrimary key
platformvarchar(255) NULLPlatform slug
namevarchar(255)Cache key
valueLONGTEXT NULLCached payload
expirationTIMESTAMP NULLExpiry time
failed_countint(11) DEFAULT 0Consecutive fetch failures (back-off)
created_atTIMESTAMP NULL
updated_atTIMESTAMP NULL

Indexes:

  • platform
  • name

See Caching for how services use this table.

wpsr_review_forms Table ​

Native review-collection form definitions. Core native forms and Pro review forms both write here. Migrator: ReviewFormsMigrator.php.

ColumnTypeComment
idINT(11) NOT NULL Auto IncrementPrimary key
titleVARCHAR(255) NOT NULLForm title
slugVARCHAR(255) NOT NULLForm slug
schemaLONGTEXT NOT NULLField schema (JSON)
settingsLONGTEXT NULLForm settings (JSON)
statusVARCHAR(20) DEFAULT 'active'active / inactive
created_byINT(11) NULLAuthor user ID
created_atTIMESTAMP NULL
updated_atTIMESTAMP NULL

Indexes:

  • status
  • slug

wpsr_optimize_images Table ​

Local image-optimization records. Migrator: ImageOptimizationMigrator.php.

ColumnTypeComment
idbigint(20) NOT NULL Auto IncrementPrimary key
platformvarchar(255) NULLPlatform slug
user_idvarchar(255)Owner user ID
user_namevarchar(255)Owner name
json_dataLONGTEXT NULLSource media metadata
fieldsLONGTEXT NULLExtra fields
media_idvarchar(1000)WordPress attachment ID(s)
sizesvarchar(1000)Generated sizes
aspect_ratioDECIMAL(4,2) NOT NULL DEFAULT 0Image aspect ratio
images_resizedtinyint(1)Resize flag
last_requestedTIMESTAMP NULLLast access time
created_atTIMESTAMP NULL
updated_atTIMESTAMP NULL

Indexes:

  • user_id
  • platform

WordPress-Native Storage ​

Not everything lives in a wpsr_ table:

DataStored inKey pattern
Templates & widgetswp_posts + wp_postmeta_wpsr_* meta keys
Plugin settingswp_optionswpsr_* (autoload no)

Verify columns before relying on them

Columns are added incrementally — migrators run alterTable() / addMissingIndexes() on upgrade. Confirm against the migration classes in database/Migrations/ for the version you target.

WP Social Ninja Developer Documentation