WordPressのサイドバーでBootstrapのパネルを使えるようにする
はまった人は結構いそうな気がするけど、誰も言及してないので書く。
問題点
Twitter Bootstrap3のパネルの構造
<div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div>
WordPressのサイトバーの引数
$args = array( 'name' => sprintf(__('Sidebar %d'), $i ), 'id' => 'sidebar-$i', 'before_widget' => '<li id="%1$s" class="widget %2$s">', 'after_widget' => '</li>', 'before_title' => '<h2 class="widgettitle">', 'after_title' => '</h2>' ); register_sidebars( $number, $args );
関数リファレンス/register sidebars - WordPress Codex 日本語版
これをそのまま使おうとすると……
register_sidebars(1, array( 'name' => 'sidebar left', 'id' => 'sidebar-left', 'before_widget' => '<div class="panel panel-default">', 'before_title' => '<div class="panel-heading">', 'after_title' => '</div>', 'after_widget' => '</div>', ));
<div class="panel-body">とその閉じタグ</div>を入れる箇所がない……
register_sidebars()にbefore_bodyとafter_body相当のものがないと表現できない。
ちなみに、WP-Bootstrapでは
WP-BootstrapというWordPress + Twitter Bootstrapの組み合わせのテーマがあるのですが、
register_sidebar(array(
'id' => 'sidebar1',
'name' => 'Main Sidebar',
'description' => 'Used on every page BUT the homepage page template.',
'before_widget' => '<div id="%1$s" class="widget %2$s">',
'after_widget' => '</div>',
'before_title' => '<h4 class="widgettitle">',
'after_title' => '</h4>',
));
パネルは使ってないですね。
おのれ、逃げおったな!
それでもパネルを使いたい!
力技でやるなら
register_sidebars(1, array( 'name' => 'sidebar left', 'id' => 'sidebar-left', 'before_widget' => '<div class="panel panel-default">', 'before_title' => '<div class="panel-heading">', 'after_title' => '</div><div class="panel-body">', 'after_widget' => '</div></div>', ));
after_titleに<div class="panel-body">を入れるという方法。
ただし「タイトルを空にするとbefore_titleとafter_titleのタグが出力されない」という仕様のため、 閉じタグが一つ多くなりレイアウトが崩れる。
タイトル付けたくないのに意味の無いタイトルを付けておく必要がでる。渋い。
独自ウィジェットを作る
WP_Widgetクラスを拡張させて独自ウィジェットを作ってそれを使えば自由にレイアウトできる。
WP_Widgetのコードは長いので略。詳細は以下の公式テーマの実装を見るか、ググって。
この場合、独自ウィジェットにできないもの(標準ウィジェットや、プラグインで提供されるウィジェット)では使えない。 これもまた渋い。
ちゃんと対応する
独自テーマを修正するにあたり、もうちょっとスマートにやりたいよねということで、 WordPressのソースを追ってみた。
class WP_Widget_Text extends WP_Widget {
...
public function widget( $args, $instance ) {
/** This filter is documented in wp-includes/default-widgets.php */
$title = apply_filters( 'widget_title', empty( $instance['title'] ) ? '' : $instance['title'], $instance, $this->id_base );
/**
* Filter the content of the Text widget.
*
* @since 2.3.0
*
* @param string $widget_text The widget content.
* @param WP_Widget $instance WP_Widget instance.
*/
$text = apply_filters( 'widget_text', empty( $instance['text'] ) ? '' : $instance['text'], $instance );
echo $args['before_widget'];
if ( ! empty( $title ) ) {
echo $args['before_title'] . $title . $args['after_title'];
} ?>
<div class="textwidget"><?php echo !empty( $instance['filter'] ) ? wpautop( $text ) : $text; ?></div>
<?php
echo $args['after_widget'];
}
WordPress/default-widgets.php at master · WordPress/WordPress · GitHub
どうやらwidget_titleというフィルターが設定されているようなので、
これを使うと実現できそう。
上のソースはテキストウィジェットだけど、他のウィジェットもだいたい同じ。
フィルターを使ったパネル対応
register_sidebars(1, array( 'name' => 'sidebar left', 'id' => 'sidebar-left', 'before_widget' => '<div class="panel panel-default">', 'before_title' => '', 'after_title' => '', 'after_widget' => '</div></div>', )); add_filter('widget_title', 'widget_title_add_heading_tag'); function widget_title_add_heading_tag($title) { if (!empty($title)) { $title = '<div class="panel-heading">' . $title . '</div>'; } return $title . '<div class="panel-body">'; }
register_sidevars()にはbefore_titleとafter_titleを空で設定しておいてフィルターでタグを出力するというのがキモいですが、
これで一応タイトル文字の有無に関係なく動作します。
現状ではこの方法がベターなやり方だと思います。
もう少し良い実装があれば誰か教えてほしい。