Skip to content

[FEATURE]: Add multisite support #170

@dimitriBouteille

Description

@dimitriBouteille

Introduction

Multisite handling was removed in #XXX (replace with the cleanup PR number) because the previous implementation was silently broken: the useBasePrefix flag was dead code, and after a switch_blog() call, models targeting network-shared tables (User, UserMeta, Site, Blog, BlogVersion, SiteMeta, Signup, RegistrationLog) generated queries against non-existent per-blog tables (wp_2_users instead of wp_users, etc.).

This issue tracks reintroducing the feature cleanly.

Goals

  • Provide working models for the network-shared WordPress tables: wp_users, wp_usermeta, wp_blogs, wp_blog_versions, wp_site, wp_sitemeta, wp_signups, wp_registration_log.
  • Keep per-blog models (Post, PostMeta, Option, Comment, Term, …) bound to the active blog and follow switch_blog() automatically.
  • Behave identically on single-site installs (no regression).
  • Cover the behavior with integration tests using wp-phpunit and WP_UnitTestCase in multisite mode.

Non-goals

  • A query DSL that crosses multiple blogs in a single statement.
  • Schema migrations across the network.

Proposed approach: two connections

Register two Eloquent connections via the Resolver and let each model pick the right one:

Connection name tablePrefix Use case
default $wpdb->prefix Per-blog tables (refreshed on switch)
base $wpdb->base_prefix Network-shared tables

Implementation sketch:

  1. Resolver::connection($name) resolves $name === 'base' to a dedicated Database instance built with $wpdb->base_prefix. Default still goes to the per-blog Database instance.
  2. Re-add a switch_blog listener that refreshes only the default connection's tablePrefix in place (don't null the singleton — that churns event listeners and breaks transaction state). The base connection is left untouched.
  3. Reintroduce Models\Multisite\{Blog, BlogVersion, Site, SiteMeta, Signup, RegistrationLog} and update User, UserMeta to declare protected $connection = 'base'.
  4. Document the contract in the README: "After switch_blog($id), the next default-connection query targets the new blog automatically. Shared models always hit the network tables regardless of the current blog."

Open questions

  • In-place prefix mutation vs new connection on switch_blog. Mutating is cheaper but invalidates the prepared-statement cache and any builder that captured the connection. A clean recreate (with a stable base) is safer; benchmark before deciding.
  • Transaction safety across switch_blog. WordPress core does not expect an active DB transaction during a blog switch. We should at least detect and refuse the switch (or warn) when transactionLevel() > 0 on the default connection. To investigate.
  • HasMetas join queries on shared models. UserMeta lives on base; if a developer joins user meta with per-blog tables (e.g. Post), the current single-connection joinToMeta() will mix prefixes. Decide whether to forbid cross-connection joins or implement a manual cross-database SQL fallback.
  • global $wpdb switching. switch_to_blog() reassigns $wpdb->prefix but the wpdb object stays the same — confirm that the Database instance can rely on a single $wpdb reference for both connections.

References

Metadata

Metadata

Labels

FeatureNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions