「外観」->「ウィジェット」以外のページで、ウィジェットを設定する方法
まだ管理者によるレビューが始まってもいないので、リポジトリの利用がまだなんですが、テーマ「ShapeShifter」で使用していた投稿メタによるウィジェットエリアへの出力に、登録されているウィジェットを新たに設定して出力する機能を付けました。
ええ、まだ配布していないバージョンの話です。
標準的に備わっているウィジェットの設定画面でなくても、ウィジェットの出力は可能ですので、ページ毎にウィジェットの出力を設定できればと思いました。まぁ元々実装する予定の機能でしたが、レビューと重なったりで遅れてしまっていました。恐らく外観に影響する機能ですので、リポジトリ利用のテーマのルールには引っかからないと思いますので、出来れば管理者レビュー時についでにチェックしてみたいんですけどね。
ウィジェットの出力は公開しているページで行われるものですので、何かしら投稿タイプのページの1つで出力されると想定していますので、その一例として投稿メタによる出力を紹介しますが、応用の利く方法ですので、他にも使えると思いますよ。
グローバル変数「$wp_widget_factory」が鍵
ワードプレスの標準的な方法でウィジェットを登録すると、ワードプレスが用意したグローバル変数「$wp_widget_factory(オブジェクト)」のメンバ変数「widgets(連想配列)」に「’クラス名’ => オブジェクト」という形で全てのウィジェットのオブジェクトが取得されています。
ウィジェットは基本的にアクションフック「widget_init」で登録しますから、恐らく(ちゃんと調べていません、すみません)それ以降のタイミングであればいつでも「global $wp_widget_factory;」で、登録されている全てのウィジェットのメンバ変数やメソッド使用することが可能になります。
出力に必要なデータを揃える
公開側のページで出力するのに必要なデータが幾つかありますが、「外観」->「ウィジェット」で設定する者以外にも必要な事項があります。
先に書いておきますが、ここでは投稿メタを一応想定して解説しますが、別にワードプレスのオプションに設定することを想定しても、必要なデータは同じですので、使いたい条件に合わせてください。
必要なデータは以下の通りです。
- クラス名
- 「WP_Widget」を継承するウィジェットのクラス名です。
- ウィジェットの設定値を所有する連想配列
- ウィジェットがテキストウィジェットであれば出力するテキストなど、保存されたデータを所有する連想配列です。
- ウィジェットを囲うHTMLを所有する連想配列
- 出力されるウィジェットの前後、タイトルの前後に出力されるべきHTMLです。「before_widget」「after_widget」「before_title」「after_title」というキーに、それぞれに必要なHTMLを与えます。
これらはどれもワードプレス関数「the_widget」で使用されるべき引数となります。
ただ、最後の項目は、出力する位置によりますので、設定ページを一緒にしてしまう必要もありません。都合の良いように書いてください。
管理ページへの設置
面倒な部分はウィジェットの設定フォームとその保存方法ですが、それも既に用意されているものを使用しますので、特別難しいこともありません。
投稿メタを想定すると、編集ページにメタボックスを追加して、その中にコーディングしていくところからコーディングしていきます。
メタボックスの追加方法は「コンテンツページにおけるカスタムフィールド・メタボックス実装と使用方法を紹介」をご参考にしてください。
後は前述したグローバル変数「$wp_widget_factory」を使用して、各ウィジェットのフォームを出力できるようにします。
例えば、ループで全てのウィジェットのフォームを出力する場合、以下のようになります。
foreach( $wp_widget_factory->widgets as $class => $widget_object ) { $instance = get_post_meta( $post_id, 'widget_settings_' . $widget_object->id_base, true ); // ウィジェットのフォームを出力 $widget_object->form( $instance ); }
まぁフォームがこのように全て出力されると非常に鬱陶しいので、DIVタグなどでラッパーをかけて、条件付きで表示・非表示を切り替えるようにする方がいいです。僕はその前にSELECTタグを置いてウィジェットを選択できるようにし、JavaScriptで表示するフォームを変更できるようにしています。
また、ウィジェットのオブジェクトには親クラス「WP_Widget」で用意されている共通のメンバ変数がいくつかあります。
- id_base
- 小文字でクラス名を使っている、フォームの属性「id」や「name」の値に使用されるString
- number
- ウィジェットに与える数値です。「外観」「ウィジェット」では自動加算されていますが、自分で設定する場合は値を変更して使用します。intで初期値は「1」です。
- name
- ウィジェットの名前で、ウィジェットの選択時に表示されているものです。翻訳可能テキストですので、ユーザーに寄って表示される言語が異なります。
といった具合で、上のループの例を使用するなら「$widget_object->number = $widget_number;」のようにして値を変更したりできます。
メソッド「form」で出力されるフォームのname属性値
フォームを送信するので、それを取得するのに名前が分かっていないと保存できません。
「name」の値は以下の通りです。
<?php // メソッド「form」によって出力されるフォームのinputタグなどのname属性値 $input_name = 'widget-' . $widget_object->id_base . '[' . $widget_object->number . '][' . $input_id . ']'; ?>
ウィジェットのフォーム内では、一般的にname属性値もid属性値もウィジェットのメソッドによって最後の「$input_id」以外は生成されるようになっていますので、「$_POST」とウィジェットのナンバーまでを使って取得できるようにします。
クラス名も忘れずに
設定したいウィジェットのクラス名を一緒にフォーム送信するのを忘れないようにします。
上の例での場合、ループ中にinputタグを出力してしまいます。
<?php // 「$class」はクラス名です。 echo '<input type="hidden" name="widget-' . $widget_object->id_base . '[' . $widget_object->number . '][widget_class_name]" value="' . $class . '">'; ?>
この場合、ウィジェットのメソッドで出力されるフォームとナンバーまで同じname属性値を使用しており、「widget_class_name」というキーでクラス名を拾えるようにしています。こうしておけば、フォーム送信時に「$_POST[ ‘widget-‘ . $widget_object->id_base ]」で指定したウィジェットの設定値のみを拾えるんですよ。
問題点
汎用的にこなすにはちょっと難しい問題が1つ残っています。
実は設定項目用にJavaScriptを使って何かをポップアップさせるなどするフォームの場合、JavaScriptが上手く機能しない可能性も考えられます。まぁ僕が現在修正中の項目なんですが、配布は改善してからにしますし、その前にレビュー時にチェックしてもらおうと思っている項目でもありますので、恐らく大丈夫だと思います。
まぁ僕の場合、簡易的に作ったポップアップフォームを作って使用していたせいで、上手くポップアップできなかっただけなんですが、まぁ複雑なフォームを使用しているウィジェットがある場合は、もしかすると何かしらの不具合が起こるかもしれません。
送信した設定値を取得・保存する
上のようにして出力したフォームからデータを保存する仕組みを作ります。
投稿メタの場合だと、ページが保存される時に投稿メタの設定値も保存されるべきでしょうから、アクションフック「save_post」で設定を保存する関数をコールするようにします。
あとは非常に簡単です。
global $wp_widget_factory; // ウィジェットのナンバー $widget_number = 1; foreach( $wp_widget_factory->widgets as $class => $widget_object ) { if( $class == $selected_widget_class ) { // サニタイズ $widget_settings = $wp_widget_factory->update( $_POST[ 'widget-' . $widget_object->id_base ][ $widget_number ] ); // データを保存 update_post_meta( $post_id, 'widget_settings_' . $widget_number, $widget_settings ); } }
まぁノンスチェックもすっ飛ばしていますし、僕が実際に使っているものは、SELECTタグで「id_base」の値を取得できるようにし、それを条件にして指定ウィジェットのチェックをしていますので、自分が書いているものとはだいぶ異なるんですが、大体はこんな感じです。
アクションフック「save_post」を使用していれば「$post_id」は受け取ることが出来ます。
設置したウィジェットを出力する
あとは保存した値を使って関数「the_widget」をコールするだけです。
<?php // 保存したクラス名を取得 $class_name = get_post_meta( $post_id, 'class_name', true ); // ウィジェットのナンバー $widget_number = 1; // 保存した設定を取得 $widget_settings = get_post_meta( $post_id, 'widget_settings_' . $widget_number, true ); // 出力 the_widget( $class_name, $widget_settings, array( 'before_widget' => '<div class="widget-wrapper">', 'after_widget' => '</div>', 'before_title' => '<h5 class="widget-title">', 'after_title' => '</h5>' ) ); ?>
これで呼び出したところでちゃんとウィジェットで出力できていれば問題無いでしょう。
補足など
テーマ「ShapeShifter」では、ウィジェット前のHTMLなどウィジェットエリアのデータも取得できてしまいますので、投稿メタでウィジェットエリアに出力することが出来るようになっています。方法はウィジェットを登録した際の連想配列をそのまま使用する他、オプションに保存しておくなど多々ありますが、テーマ「ShapeShifter」は標準的なウィジェットエリアとオプショナルのウィジェットエリアに分けています。
テーマ「ShapeShifter」で使用される予定のソースコードは、リポジトリが利用できるようになるまでは公開されませんが、テーマでダメな場合はプラグインに入れて無料で公開する予定です。
最後に一応、ざっくりした解説用に書いたコードばかりですので、実際に使用する場合は、ちゃんと用途に合わせてコードを書けば大丈夫です。まぁ使う機会も少ないと思いますけどね。