独自の設定や自作プラグインを作っていてはまってしまったチェックボックス。
ワードプレスのSettings API を使っているからか、name属性に何を設定するのか、送信したデータ(配列)を取得してその値をもとにフォーム内のinputタグにchecked を付けるのはどのようにしたらよいか。
ここはホントに右往左往してしまいました。^-^;)
複数のチェックボックスがある場合の配列データの送受信と、チェック状態を保持して再び設定反映する場合の参考になりましたら幸いです。
Contents
チェックボックスの基本形
チェックボックスのHTMLはまず以下のようになりますよね。
1 2 3 |
<label><input type='checkbox' name='item_interest[]' value='旅行'>旅行</label> <label><input type='checkbox' name='item_interest[]' value='IT'>IT</label> <label><input type='checkbox' name='item_interest[]' value='遊び'>遊び</label> |
登録フォームでよくある「興味があることは何?」みたいな
複数選択できるチェックボックスの例。
ここでのポイントは、name は同じにして更に
item_interest[] と配列の形で指定すること。
name は個別にしても良いですが、数が増えれば管理も煩雑になるし、Settings API を使う中では配列にしないと難しそう。
name に item_interest[] といった配列を指定することで、
複数選択された場合でも一塊のデータとして送信される、ってことになりますね。
ただ配列といっても注意が必要で、
フォーム内のチェックボックスは、送信ボタンが押された後、チェックされた項目のみがデータとして送信される。
実際に送信されたデータ($item_interest)を get_option() で取得して
var_dump() でデータの中身を見てみると...
1 2 3 4 5 6 7 8 |
<?php //データ取得 $interest = get_option( 'item_interest', [] ); var_dump( $interest ); ?> <label><input type='checkbox' name='item_interest[]' value='旅行'>旅行</label> <label><input type='checkbox' name='item_interest[]' value='IT'>IT</label> <label><input type='checkbox' name='item_interest[]' value='遊び'>遊び</label> |
- ①:なにもチェックされなければ「空のデータ」(string)
- ②~④:1つでもチェックされれば「配列データ」(array)
つまり、送られるデータは常に配列の形になっているわけではないので、
データを取得して設定にその状態を反映させる時は、
それを念頭に処理するってことになりますね。
必ず配列になるようにする
送信されたデータをもとに、それを設定に反映させたい(チェック状況を保持したい)という場合には、配列をうまく使うと簡単にできます。
ただ上で見たように、データが空の場合とか配列になってない場合があるので、必ず配列となるようにしておきます。(ここが分からず結構ハマった点)
1 2 3 4 5 6 7 8 |
//データ取得 $interest = get_option( 'item_interest', [] ); //必ず配列になるようにチェック $interest = isset( $interest ) ? (array) $interest : []; ?> <label><input type='checkbox' name='item_interest[]' value='旅行'>旅行</label> <label><input type='checkbox' name='item_interest[]' value='IT'>IT</label> <label><input type='checkbox' name='item_interest[]' value='遊び'>遊び</label> |
- 2行目:データを get_option で取得。
データがない場合は空の配列になるよう初期値を第二引数で指定。
(Settings API を使う場合には、register_setting で初期値を設定しておけばよく、その場合にはこの第二引数はいらないかな... ?) - 4行目:三項演算子を使って、配列でない場合は配列へ型変換する
(「条件式 ? B : C ;」という形で、条件式が正しい場合B、正しくない場合はC にする)
- 1)isset( $interest ):
$interest に対して、データがセットされているかnullかどうか調べる - 2)(array) $interest:
データがセットされていたりnullではない場合、型を配列にする
(配列のデータがあればそのまま返すし、上の方の①のように、何もチェックされていない場合(長さ0の文字列がある場合)も配列にする) - 3)[]:
null の場合(中身が何も存在しない場合)では、空の配列([])にする
- 1)isset( $interest ):
関数リファレンス/get option(ワードプレス公式)
関数リファレンス:isset(PHPマニュアル)
言語リファレンス:三項演算子(PHPマニュアル)
このように必ず配列にしておけば、
checked を付けるときに配列の関数が使えて簡単になる。
このコードを使って、実際に送信されたデータ($item_interest)を
get_option() で取得してvar_dump() で何がセットされているか見たのが以下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php //データ取得 $interest = get_option( 'item_interest', [] ); echo '1)前: '; var_dump( $interest ); echo '<br>'; //必ず配列になるようにチェック $interest = isset( $interest ) ? (array) $interest : []; echo '2)後: '; var_dump( $interest ); echo '<br><br>'; ?> <label><input type='checkbox' name='item_interest[]' value='旅行'>旅行</label> <label><input type='checkbox' name='item_interest[]' value='IT'>IT</label> <label><input type='checkbox' name='item_interest[]' value='遊び'>遊び</label> |
- 【1)前】送信したデータのままを表示
- 【2)後】:配列へキャストしたものを表示
- ①:初期状態は、指定している通り空の配列。
(まだ一度も送信ボタンが押されてない状態。
初期値は(ここでは)get_option の第二引数で空の配列を指定) - ②:なにもチェックされてない状態でデータが送信された場合。
(なにもチェックされず送信ボタンが押された場合)
文字の長さ0を持つ配列になる。 - ③~⑤:1つでもチェックされていれば、まぁ配列のまま変わらず。
これでどの場合も配列になるようにできました。
こうしておけば配列の関数を使って checked も簡単に付けられそう。
checkedをセットする
送信データ($item_interest)をもとに、チェックされた状態を設定画面で保持して表示するために「checked」を付けてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php //データ取得 $interest = get_option( 'item_interest', [] ); //必ず配列になるようにチェック $interest = isset( $interest ) ? (array) $interest : []; ?> <label><input type='checkbox' name='item_interest[]' <?php checked( in_array( '旅行', $interest ), true ); ?> value='旅行'>旅行</label> <label><input type='checkbox' name='item_interest[]' <?php checked( in_array( 'IT', $interest ), true ); ?> value='IT'>IT</label> <label><input type='checkbox' name='item_interest[]' <?php checked( in_array( '遊び', $interest ), true ); ?> value='遊び'>遊び</label> |
8行目、11行目、14行目で使っているのが、
checked関数と in_array関数。
まず in_array関数で、データ(配列)の中に指定する文字列が含まれるかどうかをチェック。
( in_array( ’対象の文字列’ , 配列 ) で、
配列内に対象の文字列があれば true を返す)
関数リファレンス:in_array(PHPマニュアル)
「チェックボックスのvalueで指定している文字列」を「対象の文字列」にすれば、そのチェックボックスがチェックされているかどうかわかりますね。
チェックボックスではチェックされる個数によって送信される配列の中の個数も変わりますが、このin_array関数を使えば個数が変わってもチェックできるのが嬉しい。
in_array関数で調べた結果(trueかどうか)をもとに checked関数を使って「checked="checked"」を入れる。
( checked( 'A', 'B' ) で A, B を比較して、同じであれば
「半角スペース」+「checked="checked"」を返す。同じでなければ何も返さない)
関数リファレンス:checked(英語)
in_array()では対象の文字列があれば true を返すので、
checked()では true と比較すればよい、となりますね。
以上で完成!やったね。^◇^)ゞ
チェックボックスでは、
nameを個別にして <input type="hidden" ~> を並列に並べて個別のnameのチェック状態を調べる、という場合もありますね。
1 2 |
<input type="hidden" name="name1" value="off"> <input type="checkbox" name="name1" value="on">項目1 |
name が同じものはペアになり、
チェックされれば valueに設定されている値(ここでは「on」)が送られ、
チェックされてなければ <input type="hidden" value="xxx"> のvalue に設定されている値(ここでは「off」)が送られる。
でも複数のチェックボックスがあり、
これと同じことをしようとすると...
1 2 3 4 5 6 |
<input type="hidden" name="name1" value="off"> <input type="checkbox" name="name1" value="on">項目1 <input type="hidden" name="name2" hidden value="off"> <input type="checkbox" name="name2" value="on">項目2 <input type="hidden" name="name3" hidden value="off"> <input type="checkbox" name="name3" value="on">項目3 |
このように name の値を変える必要が出てきて、
項目が増えるにつれて管理が大変なことになるし、
上で見たように name を配列にすると、hidden もペアにするために name に同じ配列を設定することになり、データがうまく送信できない。
実際以下のようにしてデータがどうなるか見てみた。
(hidden のinput要素含めて name の値はすべて同じ。明らかにうまくいかなさそう笑)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php //データ取得 $interest = get_option( 'item_interest', [] ); //必ず配列になるようにチェック $interest = isset( $interest ) ? (array) $interest : []; echo '$interest:<br> '; var_dump($interest); echo '<br><br>'; ?> <input type='hidden' name='item_interest[]' value='off'> <label><input type='checkbox' name='item_interest[]' value='旅行'>旅行</label> <input type='hidden' name='item_interest[]' value='off'> <label><input type='checkbox' name='item_interest[]' value='IT'>IT</label> <input type='hidden' name='item_interest[]' value='off'> <label><input type='checkbox' name='item_interest[]' value='遊び'>遊び</label> |
すべてのチェックボックスがチェックされてない状態(①)では、
送信されるデータもすべて「off」の配列になっていて、これはまぁOK。
でもチェックボックスにチェックを入れると、
<input type="hidden" name="xxxx"> の name が全て同じなのでうまく送信できず、配列の個数が増えていき、期待する配列のデータにはならない。
例えば②の「旅行」だけにチェックを入れた場合、
よく見ると off の値も配列に含まれてしまってます。^-^;)
これでもチェックした項目の value が配列に入るので、見た目の動作的にはうまく動いているようにはなりますが、想定通りの配列とならないのでトラブルの元にもなりそうな予感。
高速化の対応も考えてみる
in_array()を使ってチェックされているかどうか調べるのは簡単で分かりやすいですが、in_array()は動作が遅い、というお話もありますね。
(動作的には配列の中を1つ1つサーチして結果を返すのでそうなのでしょう。
以下の記事が凄く参考になる。
PHPの連想配列は常にin_arrayより速いのか - hnwの日記)
チェックボックスの項目数が5個や10個などでは気にすることもないと思いますが、初めから早く動作するコードにしておく、というのも良さそうです。
その1) isset を使う
in_array() の代わりに isset() を使ってみた例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php //データ取得 $interest = get_option( 'item_interest', [] ); //必ず配列になるようにチェック $interest = $interest != null ? (array) $interest : [] ; //配列のキーと値を反転 $flip = array_flip( $interest ); ?> <label><input type='checkbox' name='item_interest[]' <?php checked( isset( $flip[ '旅行' ]), true ); ?> value='旅行'>旅行</label> <label><input type='checkbox' name='item_interest[]' <?php checked( isset( $flip[ 'IT' ]), true ); ?> value='IT'>IT</label> <label><input type='checkbox' name='item_interest[]' <?php checked( isset( $flip[ '遊び' ]), true ); ?> value='遊び'>遊び</label> |
- 7行目:
array_flip() を使って、キーと値を反転して連想配列にする - 10行目、13行目、16行目:
isset()を使って、キーのありなしでチェックされているか判断
(キーがあればチェックされていると分かる)
関数リファレンス:isset(PHPマニュアル)
関数リファレンス:array_flip(PHPマニュアル)
やってることは単純で、
・チェックボックスのデータ($interest)を連想配列にして、
・キーがあるかないかだけをチェックする(ここで高速化)
・キーがあれば、checked を付ける
チェックボックスのデータ(3行目)、
配列へ変換(5行目)、
連想配列へ変換(7行目)
は以下のようになります。
チェックボックスの データ | 配列へ 変換 | 連想配列へ 変換 |
array() 初期値(空の配列) (初期状態) | array() | array() |
”” 何も選択されず 送信(空文字) | array() | array() |
array('IT') 1つチェック されて送信された | array('IT') | array( 'IT' => 0 ) |
array('IT','遊び') 2つチェック されて送信された | array('IT','遊び') | array( 'IT' => 0, '遊び' => 1 ) |
array('旅行','IT','遊び') 3つチェック されて送信された | array('旅行','IT','遊び') | array( '旅行' => 0, 'IT' => 1, '遊び' => 2 ) |
このように連想配列に変換しておけば、
どの場合でも isset()でキーのありなしが調べられて、チェックボックスのチェック状態も簡単に分かるってわけですね。
その2)array_key_exists を使う
isset() 同様、キーのありなしを見るだけなら
array_key_exists() を使っても良さそうです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php //データ取得 $interest = get_option( 'item_interest', [] ); //必ず配列になるようにチェック $interest = $interest != null ? (array) $interest : [] ; //配列のキーと値を反転 $flip = array_flip( $interest ); ?> <label><input type='checkbox' name='item_interest[]' <?php checked( array_key_exists( '旅行', $flip ), true ); ?> value='旅行'>旅行</label> <label><input type='checkbox' name='item_interest[]' <?php checked( array_key_exists( 'IT', $flip ), true ); ?> value='IT'>IT</label> <label><input type='checkbox' name='item_interest[]' <?php checked( array_key_exists( '遊び', $flip ), true ); ?> value='遊び'>遊び</label> |
isset() とは少し書き方が変わるだけで、やってることは同じ。
関数リファレンス:array_key_exists(PHPマニュアル)
isset() と array_key_exists() は 処理スピード的には基本同等のようですが、array_key_exists は関数の分だけ場合によっては遅くなることもあるようです。
(isset は if や echo などと同じく言語構造の1つ(phpの言語としてもともと組み込まれているもの)で、関数よりも先に見に行くことからそれだけ速い)
また、isset() はキーの値が null の場合、キーがあっても false を返すのに対して、array_key_exists() はキーがあればキーの値にかかわらず true を返す、というのが違うところ。
(つまり、キーのありなしだけをしっかりチェックしたい場合には array_key_exists()を使う)
上のコードでは、array_flip で反転させて連想配列にしているので、キーに対して null はないことから、まぁどちらを使っても良さそうです。
ということから 言語構造の isset か、関数のarray_key_exists で比べれば、気持ち的には isset の方が早そうなので、高速化まで考える場合には isset を使っておけばよいのかな、という感じ。
ではでは~ ^◇^)ゞ