2026年6月1日

2026年6月1日

WordPressマルチサイトでサブサイト間のデータを共有する方法(switch_to_blog・ネットワークオプション・ユーザーメタ)

WordPressマルチサイトでサブサイト間のデータを共有するには、共有したいデータの種類に合わせて仕組みを選ぶのが近道です。ネットワーク全体の設定値は get_site_option()(ネットワークオプション)、ほかのサイトの投稿は switch_to_blog() で切り替えて取得、ユーザーに紐づく情報はネットワーク共通のユーザーメタ、が基本の組み合わせです。この記事では、それぞれの使い方とコード例、switch_to_blog() の落とし穴、キャッシュの持たせ方、プラグインに任せる場合の考え方を説明します。

マルチサイトのデータは何が共有され、何が分かれているか

最初に、データベースの構造を押さえておくと選択に迷いません。マルチサイトでは、サイトごとに wp_2_posts、wp_2_options のようにテーブルが分かれます。一方で、ユーザー(wp_users・wp_usermeta)とネットワーク全体の設定(wp_sitemeta)は全サイトで共通です。

データ 保存場所 共有のされ方
投稿・固定ページ・メディア サイトごとの wp_{ID}_posts など サイトごとに独立
サイトの設定(オプション) サイトごとの wp_{ID}_options サイトごとに独立
ユーザーとユーザーメタ wp_users・wp_usermeta ネットワーク全体で共通
ネットワークの設定 wp_sitemeta ネットワーク全体で共通

ユーザーは共通でも、権限グループ(管理者・編集者など)はサイトごとに別です。あるサイトで管理者でも、別のサイトでは権限がない、ということが普通に起こります。

方法1:ネットワーク全体の設定はネットワークオプションに置く

全サブサイト共通の電話番号、会社情報、外部サービスの共通IDのような設定値は、サイトごとのオプションではなくネットワークオプションに保存します。

// 取得(第2引数は値がない場合の既定値)
$tel = get_site_option( 'my_network_tel', '' );

// 保存
update_site_option( 'my_network_tel', sanitize_text_field( $value ) );

関数名に site と付いていますが、これは歴史的な経緯による名前で、実際にはネットワーク単位の設定を読み書きします。テーマやプラグインのどのサブサイトから呼んでも同じ値が返ります。

値の編集画面は、ネットワーク管理画面に network_admin_menu フックでページを追加して作るのが一般的です。保存処理では check_admin_referer() でnonceを検証し、current_user_can( 'manage_network_options' ) で権限を確認してから update_site_option() を呼びます。ネットワーク管理画面のフォームは通常の設定APIの保存先(options.php)が使えないため、network_admin_edit_{アクション名} フックで保存処理を受けるのが定番の作り方です。

画面を作るほどでもなければ、WP-CLIで直接設定することもできます。

wp site option update my_network_tel '03-0000-0000'
wp site option get my_network_tel

特定のサブサイトのオプションを別のサイトから読みたいだけなら、get_blog_option( $blog_id, 'blogname' ) のように、サイトIDを指定して取得する関数もあります。

方法2:ほかのサイトの投稿はswitch_to_blog()で取得する

メインサイトのトップページに各サブサイトの新着記事を並べる、といった用途では switch_to_blog() で対象サイトに切り替え、通常のクエリで取得してから restore_current_blog() で戻します。

/**
 * ネットワーク内の公開サイトから新着記事を集める。
 */
function my_network_recent_posts( $per_site = 3 ) {
	$cached = get_site_transient( 'my_network_recent_posts' );
	if ( false !== $cached ) {
		return $cached;
	}

	$items = array();
	$sites = get_sites( array(
		'public'   => 1,
		'archived' => 0,
		'spam'     => 0,
		'deleted'  => 0,
		'number'   => 50,
	) );

	foreach ( $sites as $site ) {
		switch_to_blog( $site->blog_id );

		$posts = get_posts( array(
			'post_type'      => 'post',
			'post_status'    => 'publish',
			'posts_per_page' => $per_site,
		) );

		foreach ( $posts as $p ) {
			$items[] = array(
				'site'  => get_bloginfo( 'name' ),
				'title' => get_the_title( $p ),
				'url'   => get_permalink( $p ),
				'date'  => get_post_time( 'U', true, $p ),
			);
		}

		restore_current_blog(); // 1回切り替えたら1回戻す
	}

	usort( $items, function ( $a, $b ) {
		return $b['date'] - $a['date'];
	} );

	set_site_transient( 'my_network_recent_posts', $items, 15 * MINUTE_IN_SECONDS );
	return $items;
}

// どのサイトで記事の公開状態が変わってもキャッシュを消す
add_action( 'transition_post_status', function ( $new_status, $old_status ) {
	if ( 'publish' === $new_status || 'publish' === $old_status ) {
		delete_site_transient( 'my_network_recent_posts' );
	}
}, 10, 2 );

表示側では、配列の値を esc_html() と esc_url() でエスケープして出力します。このコードはネットワーク有効化したプラグインか、mu-pluginに置くと全サイトで同じ関数とキャッシュ削除の処理が動きます。

switch_to_blog()でつまずきやすい点

切り替わるのはデータベースの参照先だけ: switch_to_blog() はテーブルの接頭辞などを切り替えますが、読み込まれているテーマやプラグインは元のサイトのままです。切り替え先のサイトだけで登録しているカスタム投稿タイプやタクソノミー、そのサイトのプラグインが追加するフィルターは働きません。切り替え先でしか登録されていない投稿タイプの記事は、get_permalink() の結果が期待と違う形になることがあります。共有したい投稿タイプは、ネットワーク有効化したプラグインで全サイトに同じ定義を登録しておくと扱いやすくなります。

戻し忘れと入れ子: switch_to_blog() を2回呼んだら restore_current_blog() も2回必要です。戻し忘れると、そのリクエストの後半すべてが別サイトのデータを参照してしまいます。上の例のようにループの中で「切り替え→処理→戻す」を1組にして書きます。

負荷: サイトの数だけ切り替えとクエリが発生します。数十サイトを毎回のページ表示で巡回すると遅くなるため、上の例ではネットワーク共通のトランジェント(set_site_transient())で結果を15分保持し、記事の公開・非公開が変わったら削除しています。永続オブジェクトキャッシュ(RedisやMemcached)を導入している環境では、切り替えのコストも下がります。

方法3:ユーザーに紐づく情報はユーザーメタで共有する

会員番号やプロフィールの追加項目など、ユーザーごとの情報は update_user_meta() で保存すると、どのサブサイトからでも get_user_meta() で同じ値を読めます。ユーザーメタはネットワーク全体で共通のテーブルにあるためです。

update_user_meta( $user_id, 'my_member_no', sanitize_text_field( $member_no ) );
$member_no = get_user_meta( $user_id, 'my_member_no', true );

逆に、「サイトAでだけ有効な設定」をユーザーメタに保存したい場合は、キー名にサイトIDを含めるなどして、意図せず全サイトで共有されないようにします。ユーザーがどのサイトに所属しているかは get_blogs_of_user( $user_id ) で取得できます。

個人情報を複数サイトで共有する場合は、各サイトのプライバシーポリシーにその旨が反映されているかも確認してください。

方法4:専用テーブルやプラグインを使う

上の3つで足りない、たとえば「全サイト共通の商品マスタ」のような独自データは、$wpdb->base_prefix を使ったネットワーク共通の独自テーブルを作る方法があります($wpdb->prefix はサイトごとに変わり、base_prefix はネットワーク共通の接頭辞です)。テーブル作成はネットワーク有効化時に一度だけ行い、クエリは $wpdb->prepare() で組み立てます。

記事そのものを別サイトへ配信・複製したい場合は、10upが開発しているDistributorのような、サイト間で投稿を配信するプラグインも選択肢です。多言語サイトの翻訳記事を関連付けたい場合は、マルチサイト型の多言語プラグインが向いています。どちらも機能や対応範囲はバージョンによって変わるので、導入前に公式の説明を確認してください(2026年10月現在)。

作業前の確認事項

  • ネットワーク全体に影響するコードなので、データベースのバックアップを取り、ステージング環境で試してから本番に入れる
  • ネットワーク有効化したプラグインは全サイトで読み込まれるため、処理が重くないか、特定のサイトだけで動くべき処理が混ざっていないか確認する
  • get_sites() の number は既定で100件までなので、サイト数が多いネットワークでは件数とキャッシュの持たせ方を見直す

まとめ

  • ネットワーク共通の設定は get_site_option()・update_site_option()、特定サイトの設定は get_blog_option() で読む
  • ほかのサイトの投稿は switch_to_blog() で取得し、ループの中で切り替えと restore_current_blog() を1組にして書く
  • switch_to_blog() はプラグインや投稿タイプの登録までは切り替えないので、共有したい投稿タイプは全サイトで同じ定義にする
  • 集計結果は set_site_transient() でキャッシュし、更新時に削除する
  • ユーザーに紐づく情報はユーザーメタで全サイト共通になる。独自データは base_prefix の共通テーブルや配信プラグインを検討する

お気軽にご相談ください

お見積りへ お問い合わせへ
▲