ウィジェットを使うとブログのサイドバーを簡単に編集することができます。管理画面の「外観」→「ウィジェット」で設定できるいつものやつです。ほとんどのテーマで対応しているのではないでしょうか。サイドバーのデザインを管理画面から簡単に取り外しできる便利機能ですが、これを自作してみたいと思います。
管理画面で管理できるので何度もソースコードを書き直す必要がなく、サイドバーが複数あっても使いまわすことができるなどのメリットがあります。
完成予想図
ウィジェットの管理画面でEメールアドレスをテキストボックスに入力・保存し、それをサイドバーのウィジェットに表示するウィジェットアイテムを作ってみましょう。
最初に完成したソースコードを貼っておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | class My_Widget extends WP_Widget{ public function __construct() { $widget_ops = array( 'classname' => 'widget_email_test', 'description' => '入力したEメールをサイドバーに表示します。' ); parent::__construct('email-test', 'Test Widget Item', $widget_ops); } public function widget($args, $instance) { // ウィジェットタイトルの作成 $title = apply_filters('widget_title', 'Eメールテスト'); $email = $instance['email']; echo $args['before_widget']; echo $args['before_title'] . $title . $args['after_title']; ?> <p>Email: <?php echo $email; ?></p> <?php echo $args['after_widget']; } function update($new_instance, $old_instance) { $instance = $old_instance; $instance['email'] = $new_instance['email']; return $instance; } public function form($instance) { $email = $instance['email']; $email_name = $this->get_field_name('email'); $email_id = $this->get_field_id('email'); ?> <p> <label for="<?php echo $email_id; ?>">Email:</label> <input class="widefat" id="<?php echo $email_id; ?>" name="<?php echo $email_name; ?>" type="text" value="<?php echo esc_attr( $email ); ?>"> </p> <?php } } add_action('widgets_init', function() {return register_widget("My_Widget");}); |
基本
これがウィジェットの基本の形になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class MyWidget extends WP_Widget { public function __construct() { // コンストラクタ。ウィジェットを登録する。 } public function widget($args, $instance) { // サイドバーでどのように表示するか設定する。 } public function update($new_instance, $old_instance) { // 新しい設定データが適切かどうかをチェックする。 } public function update($instance) { // 管理画面でどのように表示するか設定する。 } } |
WP_Widget を継承したクラスで __construct、widget 関数、update 関数、update 関数をそれぞれオーバーライドして動作を記述していくことになります。以下、オーバーライドするメソッドをひとつずつみていきます。
__construct
古い記事ですとコンストラクタがクラス名になっている情報もあるかと思います。
1 2 3 4 5 6 7 | class MyWidget extends WP_Widget { public function MyWidget() { // コンストラクタ。ウィジェットを登録する。 } (略) } |
このコンストラクタの記述の仕方は、将来的に廃止する方向であると公式にアナウンスされているので、今のうちから __construct() を使うようにしましょう。ウィジェットアイテムを作成するクラスのコンストラクタは以下のようになります。
1 2 3 4 5 6 7 | public function __construct() { $widget_ops = array( 'classname' => 'HTML で class 属性に出力される', 'description' => 'ウィジェットの説明' ); parent::__construct('HTML で id 属性に出力される', 'ウジェットのタイトル', $widget_ops); } |
例えば、次のようなコンストラクタでウィジェットアイテムを作成したとします。
1 2 3 4 5 6 7 | public function __construct() { $widget_ops = array( 'classname' => 'output_class', 'description' => ウィジェットの説明' ); parent::__construct('output_id', 'ウジェットのタイトル', $widget_ops); } |
管理画面では次のように表示されます。
サイドバーの各ウィジェットの HTML ソースは ASIDE タグで囲まれていますが、class 属性と id 属性はそれぞれ次のようになります。
1 2 3 4 5 6 | <ASIDE class="widget output_class" id="output_id-2"> // サイドバーで出力したい HTML は // widget 関数をオーバーライドする </ASIDE> |
スタイルシートでデザインをカスタマイズする際には、これら class・id 属性を利用するので、使いやすいように決めるとよいでしょう。参考までに、他のウィジェットがどのようになっているかを見ると、
「アーカイブ」の場合:
1 | <ASIDE class="widget widget_archive" id="archives-2">...</ASIDE> |
「最近の投稿」の場合:
1 | <ASIDE class="widget widget_recent_entries" id="recent-posts-2">...</ASIDE> |
id 属性の末尾に自動で「-2」が付与されることに注意してください。ちなみに、次のように省略して記述することもできます。
1 2 3 | public function __construct() { parent::__construct(false, 'ウジェットのタイトル'); } |
日本語表示に関して補足
日本語を出力させたい場合は注意が必要です。例えば、上記のコンストラクタのソースコードを Twenty Fifteen テーマの function.php に追記しても日本語を表示してくれません。
今回は説明を簡単にするために function.php にソースコードを追加して動作確認していきますが、本来はプラグインとしてウィジェットアイテムを作成することが望ましく、ソースコードは次のように翻訳関数を使って英語表記にします。_e() のかわりに __() でもよいようです。
1 2 3 4 5 6 7 | public function __construct() { $widget_ops = array( 'classname' => 'output_class', 'description' => _e('widget of description') ); parent::__construct('output_id', _e('widget title'), $widget_ops); } |
その上で、プラグインフォルダの中に「language」フォルダを作成し、拡張子が .mo の翻訳ファイルを使って日本語に対応するというのが正しい手順です。
今回は行儀悪く、次のように対応しています。
1 2 3 4 5 6 7 | public function __construct() { $widget_ops = array( 'classname' => 'output_class', 'description' => mb_convert_encoding('ウィジェットの説明', "UTF-8", "SJIS") ); parent::__construct('output_id', mb_convert_encoding('ウジェットのタイトル', "UTF-8", "SJIS"), $widget_ops); } |
function.php を UTF-8 で保存し直しても一応動作はします。
widget 関数
サイドバーでどのように表示するのかをここで記述します。
1 2 3 4 5 6 7 8 9 10 11 12 | public function widget($args, $instance) { // ウィジェットタイトルの作成 $title = apply_filters('widget_title', 'Eメールテスト'); $email = $instance['email']; echo $args['before_widget']; echo $args['before_title'] . $title . $args['after_title']; ?> <p>Email: <?php echo $email; ?></p> <?php echo $args['after_widget']; } |
before_widget、after_widget、before_title、after_title など、見慣れないものがありますね。これらの変数は次のように対応しています。
1 2 3 4 | $args['before_widget']:ASIDE 開始タグ(<ASIDE>) $args['before_title']:H2 開始タグ(<H2>) $args['after_title']:H2 終了タグ(</H2>) $args['after_widget']:ASIDE 終了タグ(</ASIDE>) |
これらが無いとスタイルシートでの指定によっては表示が崩れることがあります。ソースコードを一部削除したりして確認してみてください。例えば、次のようなソースコードにしたとします。
1 2 3 4 5 6 7 8 9 10 | public function widget($args, $instance) { // ウィジェットタイトルの作成 $title = apply_filters('widget_title', 'Eメールテスト'); $email = $instance['email']; echo $args['before_title'] . $title . $args['after_title']; ?> <p>Email: <?php echo $email; ?></p> <?php } |
Twenty Fifteen テーマでは次のように表示が崩れます。
アクションフックでウィジェットを登録
update、form 関数についてはソースコードのままなので省略。最後に WordPress のアクションフックで作成したウィジェットを登録します。widgets_init のタイミングで register_widget 関数を使い、ウィジェットとして作成した My_Widget クラスを登録します。
1 | add_action('widgets_init', function() {return register_widget("My_Widget");}); |
PHP 5.3 から使えるようになった無名関数で記述していますが、やっていることは以下のソースコードと同じです。
1 2 3 4 | function mywidget_register() { register_widget('My_Widget'); } add_action('widgets_init', 'mywidget_register'); |
最後に、管理画面でウィジェットエリアに自分で作成したウィジェットを追加し、E メールを入力して保存、エントリーのサイドバーでちゃんと表示されることを確認してください。サイドバーで次のような HTML が出力されていれば完成です。
1 2 3 4 | <ASIDE class="widget widget_email_test" id="email-test-2"> <H2 class="widget-title">Eメールテスト</H2> <P>Email: test@test.ne.jp</P> </ASIDE> |
まとめ
今回は説明を簡単にするために省略しましたが、実際には widget、update、form の各関数内で empty、strip_tags、esc_attr 関数などを使って、入力された値が適切であるかをチェックします。自分で使う分にはなくても困りませんが、作成したプラグインを公開する場合、ユーザーが思わぬ値を入力することで、よからぬ動作をすることを防ぐ必要があるので注意してください。
WordPress をインストールしたフォルダ以下の wp-includes/default-widgets.php で他のウィジェットが定義されています。これらを参考に、ウィジェットの完成度を高めてください。
2015.10.13 追記:ウィジェットのチェックボックスがうまく動作しない
ウィジェットの設定画面でチェックボックスを表示させて、設定によりサイドバーの表示方法を変更したい場合があると思います。例えば、アーカイブなんかでも見たことありますよね。
これを実現するようなソースコードはこんな感じになると思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | class MyWidget extends WP_Widget { public function __construct() { parent::__construct(false, 'My Widget'); } public function widget($args, $instance) { $c = ! empty($instance['test']) ? '1' : '0'; echo $args['before_widget']; if ($c) { ?> <p>Check ON</p> <?php } else { ?> <p>Check OFF</p> <?php } echo $args['after_widget']; } public function update($new_instance, $old_instance) { $instance = $old_instance; $instance['test'] = $new_instance['test'] ? 1 : 0; return $instance; } public function form($instance) { $test = $instance['test'] ? 'checked="checked"' : ''; $test_id = $this->get_field_id('test'); $test_name = $this->get_field_name('test'); ?> <p> <input class="checkbox" type="checkbox" <?php echo $test; ?> id="$test_id" name="$test_name" /> <label for="$test_id"><?php echo _e('Test of Checkbox'); ?></label> </p> <?php } } add_action('widgets_init', create_function('', 'return register_widget("MyWidget");')); |
ところが、これだとうまく動作しません。こちらのリンク先でも述べられておられますが、
WordPressの自作ウィジェットの個人的な覚書 | かちびと.net
実装はされるのですが、チェックボックスにチェックが入った状態を保存してくれず、いつまでたっても input タグに「checked=”checked”」が追加されません。
これをきちんと動作させるにはソースコードを次のように修正します。修正部分は form 関数内だけなので、他の部分は省略します。
1 2 3 4 5 6 7 8 9 | public function form($instance) { $test = $instance['test'] ? 'checked="checked"' : ''; ?> <p> <input class="checkbox" type="checkbox" <?php echo $test; ?> id="<?php echo $this->get_field_id('test'); ?>" name="<?php echo $this->get_field_name('test'); ?>" /> <label for="<?php echo $this->get_field_id('test'); ?>"><?php echo _e('Test of Checkbox'); ?></label> </p> <?php } |
form 関数を置き換えてみてください。
Comment