WordPressのマルチブログで、親と子で同じウィジェットを使う時の注意点

ハマリました。そして、なんとか解決しました。
ちなみにバージョンはWordpress3.1です。結果としてバグっぽくて、最新バージョンだとフィックスされてるのかもしれませんが、サーバのphpのバージョンなどの問題で3.1を使う機会はまだあるのかもしれないと思うので、ひょっとして誰かに役立つかと思って、情報シェアの意味でメモしておきます。

今担当している案件で、マルチサイト化して親ブログのウィジェットを子ブログで共用する、という仕様を実現しようとしたのですが、子サイトで親サイトのウィジェットが表示されなくて困りました。ソースを追うほどのスキルもないのでとりあえずDBガン見していたところ、なんとか解決しました。

さてまずは一般的な手法として、マルチブログで他のブログの情報を読み込む際には次のように記述します。

<?php
switch_to_blog('切り替えたいblogのID');//目的のブログに切り替える
//そのブログでやりたい処理を記述
restore_current_blog();//本来のブログに戻す
?>

そうすると、親のウィジェットを利用したい場合は子ブログで次のように書けば動くはず

<?php
switch_to_blog('親blogのID');
dynamic_sidebar('ウィジェットの名前かID');
restore_current_blog();
?>

で、全く表示されません。あれ?・・・となりました。

色々と試行錯誤して、DBの中のwidgetとつくデータを虱潰しに見てみたら、どうやらswitch_to_blogしても、ウィジェットIDは元のブログのDBのテーブルを照合して、パーツのIDを引っ張ってるのでは?と気づきました。そこで取得したパーツIDを元に、親ブログのテーブルを見に行っている?というおかしなことになっているみたい。分かりづらいのでDBのテーブル名を交え纏めてみます。DBの接頭辞はwp_で、ブログ2に記述して、ブログ1にスイッチすると仮定。

  1. switch_to_blog(1)
  2. dynamic_sidebar(‘sidebar-1’)
  3. wp_2_options(スイッチ先ではない、自分自身の設定テーブル)のsidebars_widgetsを見に行き、一致するIDを探してそれに含まれる部品のID(text-1など)を取得
  4. wp_options(ブログ1の設定テーブル)のwidget_textを見に行き、3で取得したIDに対応する値を取得
  5. 値を返す
  6. restore_current_blog()

といった感じ。

text-1などのパーツIDはウィジェットを生成して管理画面から各パーツを配置した際に自動で付番される通番であり、ブログごとの任意の値です。ウィジェットのデータはそのパーツIDに紐付けられてDBに格納されているのですが、どのウィジェットにどのパーツが入っているかの組み合わせ情報はまたさらに別テーブルにある。そのテーブルを見に行く際、恐らく自分自身のテーブルを見に行っているみたいで、必要とするパーツIDが取得できないか、または、ずれているという状況が起こっているのではないかと。

たぶんこれ、一番スマートな解決策はswitch_to_blogしてウィジェットを呼んだ際に、最初に見に行くテーブルをちゃんとスイッチ先のテーブルを見に行くように修正すればよいと思うのですが、そんな技術は無いので、今回はDBの値を書き直すことで対応しました。

このバグを回避するためには、各ブログに個別にウィジェットが存在し、そのウィジェットとその中身のパーツの組み合わせが、各ブログ全てで一致していれば問題無いと思われます。で、DBにテーブルのwp_X_optionsにある、sidebars_widgetsとwidget_textに含まれるパーツIDのを全て手動で一致させたところ、意図通りの動きをするようになりました。

具体的な値の例は次のとおり

ブログ1

wp_optionsのsidebars_text

ウィジェットの名前とパーツのIDを格納してる行の値の抜粋です。

a:15:{s:19:”wp_inactive_widgets”;a:13:{i:0;s:7:”pages-2″;i:1;s:10:”calendar-2″;i:2;s::”links-2″;
i:3;s:6:”text-2″;i:4;s:5:”rss-2″;i:5;s:11:”tag_cloud-2″;i:6;s:10:”nav_menu-2″;i:7;s:8:”search-2″;
i:8;s:14:”recent-posts-2″;i:9;s:17:”recent-comments-2″;i:10;s:10:”archives-2″;i:11;s:2:”categories-2″;i:12;s:6:”meta-2″;}
s:9:”sidebar-1“;a:1:{i:0;s:7:”text-1“;}
s:9:”sidebar-2“;a:1:{i:0;s:7:”text-2“;}
s:13:”array_version”;i:3;}

wp_optionsのsidebars_widgets

パーツのIDでデータを実際に格納してる行の値の抜粋です。

a:15:{i:2;a:0:{}
i:1;a:3:{s:5:”title”;s:0:””;s:4:”text”;s:XXX:”ここにtext-1のテキスト”;s:6:”filter”;b:0;}
i:2;a:3:{s:5:”title”;s:0:””;s:4:”text”;s:XXX:”ここにtext-2のテキスト”;s:6:”filter”;b:0;}
s:12:”_multiwidget”;i:1;}

これに対して、ブログ2のウィジェットとパーツのIDを揃えたいので、予めfunctions.phpや管理画面でウィジェットの設定を行った後、出来上がったDBの中身をブログ1に合わせて以下のように書き換えました。

ブログ2

wp_2_optionsのsidebars_text

a:15:{s:19:”wp_inactive_widgets”;a:13:{i:0;s:7:”pages-2″;i:1;s:10:”calendar-2″;i:2;s::”links-2″;
i:3;s:6:”text-2″;i:4;s:5:”rss-2″;i:5;s:11:”tag_cloud-2″;i:6;s:10:”nav_menu-2″;i:7;s:8:”search-2″;
i:8;s:14:”recent-posts-2″;i:9;s:17:”recent-comments-2″;i:10;s:10:”archives-2″;i:11;s:2:”categories-2″;i:12;s:6:”meta-2″;}
s:9:”sidebar-1“;a:1:{i:0;s:7:”text-1“;}
s:9:”sidebar-2“;a:1:{i:0;s:7:”text-2“;}
s:13:”array_version”;i:3;}

wp_2_optionsのsidebars_widgets

a:15:{i:2;a:0:{}
i:1;a:3:{s:5:”title”;s:0:””;s:4:”text”;s:XXX:””;s:6:”filter”;b:0;}
i:2;a:3:{s:5:”title”;s:0:””;s:4:”text”;s:XXX:””;s:6:”filter”;b:0;}
s:12:”_multiwidget”;i:1;}

こうすることで、スイッチ先ではなく、自分自身のsidebars_textを一旦見に行き、取得したパーツIDでスイッチ先の値を見に行ったとしても、IDが揃ってるのでちゃんと目的の値を取得することができ、ウィジェットが表示されます。

僕も自分でわからないなりに噛み砕いて書いたつもりなのですが、元々詳しくない人(自分含め)にはさらにわかりにくいものとなっているような・・・

このバグってWordpressのフォーラムでも何度か質問が出ているのに誰も答えていないという状況で、ネットに情報が無くて困っている人がいるかもと思いなんとか伝わるようにと頑張ってみましたが、伝わるといいのですが。

以上、いつもどおり自分用のまとめの意味合いが強めのエントリーでした。