べんりワザ・WordPressのマルチサイト

マルチサイトで他サイトの投稿を取得する(switch_to_blog)

マルチサイトで別のサイトの投稿や設定を読む方法。switch_to_blogと後始末の書き方、全サイトの新着をまとめる実例、重くしないためのキャッシュまでコード付きで解説します。

マルチサイトでは、switch_to_blog()で一時的に別サイトへ切り替えて投稿を取得できます。「本部サイトのお知らせを支店サイトに出す」「全サイトの新着をトップにまとめる」といった横断表示の基本です。

<?php
switch_to_blog(1);                 // ← 対象サイトのID(1はメインサイト)

$query = new WP_Query([
  'posts_per_page' => 5,
]);
$items = [];
while ($query->have_posts()) {
  $query->the_post();
  $items[] = [
    'title' => get_the_title(),
    'url'   => get_permalink(),     // ← 切り替え中に取るのが大事
    'date'  => get_the_date('Y.m.d'),
  ];
}
wp_reset_postdata();

restore_current_blog();            // ← 必ず戻す
?>

押さえるのは2つ——restore_current_blog()を必ず呼ぶこと、URLや画像は切り替え中に取得すること。

出力は「戻したあと」に

上のコードのようにデータを配列に集めてから表示すると、テンプレート側がすっきりします。

<?php if ($items) : ?>
  <ul class="cross-site-news">
    <?php foreach ($items as $item) : ?>
      <li>
        <time><?php echo esc_html($item['date']); ?></time>
        <a href="<?php echo esc_url($item['url']); ?>"><?php echo esc_html($item['title']); ?></a>
      </li>
    <?php endforeach; ?>
  </ul>
<?php endif; ?>

アイキャッチのHTMLも切り替え中に文字列として作っておくのがコツです(URLがそのサイトのものになります)。

$items[] = [
  'title' => get_the_title(),
  'url'   => get_permalink(),
  'thumb' => get_the_post_thumbnail(get_the_ID(), 'medium'),   // HTMLごと保持
];

サイトの一覧を取得する(get_sites)

<?php
$sites = get_sites([
  'number'  => 100,          // 取得件数(既定100)
  'public'  => 1,            // 公開サイトのみ
  'archived' => 0,
  'deleted'  => 0,
]);

foreach ($sites as $site) {
  echo esc_html($site->blog_id) . ' / ';
  echo esc_html($site->domain) . esc_html($site->path) . ' / ';
  echo esc_html(get_blog_option($site->blog_id, 'blogname')) . '<br>';
}
?>

get_sites()が返すのはWP_Siteオブジェクトの配列で、blog_id domain pathが入っています。サイト名や設定値はget_blog_option()で取れます(切り替え不要)。

実例:全サイトの新着をまとめる

<?php
/**
 * ネットワーク全体の新着記事を集める。
 * 重い処理なので結果をキャッシュする。
 */
function mynet_network_recent_posts($limit = 6) {
  $cache_key = 'mynet_network_recent_' . $limit;
  $cached = get_site_transient($cache_key);
  if ($cached !== false) {
    return $cached;
  }

  $items = [];
  foreach (get_sites(['number' => 50, 'public' => 1, 'archived' => 0, 'deleted' => 0]) as $site) {
    switch_to_blog((int) $site->blog_id);

    $q = new WP_Query(['posts_per_page' => $limit, 'ignore_sticky_posts' => true]);
    while ($q->have_posts()) {
      $q->the_post();
      $items[] = [
        'title'     => get_the_title(),
        'url'       => get_permalink(),
        'timestamp' => get_the_time('U'),
        'site_name' => get_bloginfo('name'),
      ];
    }
    wp_reset_postdata();

    restore_current_blog();
  }

  // 日付の新しい順に並べて上位だけ返す
  usort($items, fn($a, $b) => $b['timestamp'] <=> $a['timestamp']);
  $items = array_slice($items, 0, $limit);

  set_site_transient($cache_key, $items, 10 * MINUTE_IN_SECONDS);
  return $items;
}

ポイントは3つです。

  • サイトごとにswitch→取得→restoreを1組で回す
  • 並べ替えは集め終わってから(サイトをまたぐ並び替えはPHP側で行う)
  • 結果をキャッシュするset_site_transient()はネットワーク共通のキャッシュ)

メインサイト(親サイト)を参照する

<?php
$main_id = get_main_site_id();       // メインサイトのID
$is_main = is_main_site();           // 今メインサイトを表示しているか

if (!$is_main) {
  switch_to_blog($main_id);
  $notices = get_posts(['numberposts' => 3, 'category_name' => 'important']);
  $data = array_map(fn($p) => [
    'title' => get_the_title($p),
    'url'   => get_permalink($p),
  ], $notices);
  restore_current_blog();
}
?>

「本部の重要なお知らせを全支店サイトのトップに出す」——マルチサイトでいちばん需要のあるパターンです。

切り替えずに取れるもの

1つの値を取るだけならswitch_to_blog()は不要です。こちらのほうが軽くなります。

<?php
echo esc_html(get_blog_option(3, 'blogname'));      // サイト3のサイト名
echo esc_url(get_site_url(3));                      // サイト3のURL
echo esc_url(get_home_url(3, '/contact/'));         // サイト3の任意のパス
$details = get_blog_details(3);                     // ドメイン・パスなどまとめて
?>
やりたいこと切り替え使う関数
投稿を取得する必要switch_to_blog()WP_Query
サイト名・設定値を取る不要get_blog_option()
URLを作る不要get_site_url() / get_home_url()
サイトの一覧不要get_sites()
ネットワーク共通の設定不要get_site_option()

画像URLの注意

画像はuploads/sites/<ID>/にサイトごとに保存されるため、URLもサイトごとに違います。切り替えていない状態でwp_get_attachment_image()にIDを渡すと、今のサイトの画像を探して見つからないという結果になります。

画像のHTMLは切り替え中に作って持ち回る——これを守れば事故りません。

べんりワザアイキャッチ画像の出し方と代替画像アイキャッチの出し方の基本はこちら

集計・全サイト横断の処理をするとき

「全サイトの投稿数を数える」ような処理は、サイト数×クエリになるため負荷が高いです。

  • 管理画面の画面表示では避ける——WP-Cronや手動実行のバッチで集計し、結果をsite_optionに保存する
  • 表示側は保存済みの値を読むだけにする

まとめ

  1. 他サイトの投稿はswitch_to_blog($id)→取得→restore_current_blog()の1組で取る
  2. URL・画像のHTMLは切り替え中に作る。表示は戻したあとで
  3. サイト一覧はget_sites()値を1つ取るだけならget_blog_option()で切り替え不要
  4. サイト横断の集計は重い——set_site_transient()でキャッシュする

横断表示ができるようになると、マルチサイトの「まとめて運営できる」利点を活かしきれます。

よくある質問

マルチサイトで他サイトの投稿を取得するには?
switch_to_blog($blog_id)で対象サイトに切り替えてWP_Queryなどで取得し、必ずrestore_current_blog()で戻します。切り替え中に取得したパーマリンクや画像URLは、そのサイトのURLで生成されます。
restore_current_blog()を忘れるとどうなりますか?
以降の処理が別サイトの文脈のまま進み、リンク先や表示内容が入れかわります。switch_to_blogとrestore_current_blogは必ず1対1で書いてください。
子サイトの一覧を取得する関数は?
get_sites()です。返ってくるのはWP_Siteオブジェクトの配列で、blog_id・path・domainが入っています。件数指定は'number' => 100のように渡します(既定は100件)。
他サイトのサイト名や設定値だけ知りたいときは?
get_blog_option($blog_id, 'blogname')やget_site_url($blog_id)を使えば、switch_to_blogを使わずに1つの値を取得できます。切り替えはコストがかかるので、値を1つ取るだけなら避けるほうが効率的です。