wordpress:複数のカスタムフィールドをまとめて取得する
wordpressのカスタマイズを行う際、必ずと言っていいほどカスタムフィールドを使います。
それも複数使うことが多く、時には十数個使うことも。
通常であればget_post_meta()を複数回使ったり、get_post_custom()で一気に取得したりしますがどちらもちょっとめんどくさかったりします。
get_post_meta()
$key1 = get_post_meta( $post->ID , 'key1' , true); $key2 = get_post_meta( $post->ID , 'key2' , true); ... $key10 = get_post_meta( $post->ID , 'key10' , true);
こりゃめんどくさい。データベースにも負担だし。
get_post_custom()
Array ( [_edit_lock] => Array ( [0] => 1454683503:1 ) [_edit_last] => Array ( [0] => 1 ) [key1] => Array ( [0] => val1 ) [arraykey] => Array ( [0] => a:3:{i:0;s:4:"val1";i:1;s:4:"val2";i:2;s:4:"val3";} ) )
という感じでいらないものまで取得しちゃいます。シリアライズされたそのまんまだし。
という制作上の面倒な点を解消するべく複数の指定したカスタムフィールドを1回でまとめて取得するfunctionを作成してみました。
※2019/12/15追記
get_post_meta()関数でデータを取りにいく際、wp_cache_get()という関数を通ってキャッシュから取得するみたい。
このキャッシュ関数でキャッシュを生成するタイミングは調べてないですが、とにかくまとめてデータをキャッシュしてるみたいです。
なので上で書いたような「データベースにも負担だ」ということはほぼなさそう…勉強不足も甚だしい…
ということでget_post_meta()を使いながらもまとめて取得するコードを書きましたんでそちら使うと簡単かも。
コード
下記コードをfunctions.phpに加えてください。
function get_post_meta_multiple( $args , $post_id) { global $wpdb; //$post_idをエスケープ処理 $post_id = $wpdb->prepare('%d', $post_id); //$argsをエスケープ処理 foreach($args as $val){ $keys[] = $wpdb->prepare('%s', $val); } $keys = implode(',', $keys); //データベースから取得 $getmetas = $wpdb->get_results( " SELECT meta_key,meta_value FROM $wpdb->postmeta WHERE post_id = $post_id AND meta_key IN ($keys) " ); //結果を整形 $metas = array(); if( !empty( $getmetas) ) { foreach( $getmetas as $meta ) { //値がシリアライズされているか $value = ( is_serialized( $meta->meta_value ) ) ? unserialize( $meta->meta_value ) : $meta->meta_value ; $metas[ $meta->meta_key ] = $value; } //見つからなかったフィールドがあった場合、$metas[見つからなかったキー]にnullを入れる $none = array_diff( $args , array_keys( $metas ) ); if( !empty( $none ) ) { foreach( $none as $key ) { $metas[ $key ] = null; } } } //データをオブジェクトで返す return (object)$metas; }
説明ですが、コメントに書いてあるとおりです。
ちょっと細くしますと、$wpdb->prepareはSELECTにかける値をエスケープします。
SQL インジェクション攻撃からクエリを保護するためには、実行する前にクエリデータはすべて SQL エスケープする必要があります。
キーが1つの場合はこう書けばいいんですが、
$getmetas = $wpdb->get_results($wpdb->prepare( " SELECT meta_key,meta_value FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s ", $post_id , $key ));
今回は値の複数指定であるINで指定しているのでこのやりかただとうまくいきませんでした。
その他は「結果を整形」のところにあるunserializeですが、wordpressのデータベースを見てる人はわかると思いますが、配列をデータベースに入れる際はシリアライズされたカタチで入ります。
a:3:{i:0;s:4:"val1";i:1;s:4:"val2";i:2;s:4:"val3";}
こんなやつ。
これをphpで扱える配列のカタチに戻してやるのがunserializeです。
is_serializedでシリアライズされてるか調べて、シリアライズされていれば元に戻すということです。
//見つからなかったフィールドがあった場合~のところは、見つからなかったキーは$metasには入りませんのでデータを使用した場合エラーが出ます。
その回避として見つからなかったキーにnullを入れて作成します。
使い方
使い方はいたって簡単。
例のデータ
- key1 → val1
- key2 → val2
- key3 → val3
- arraykey → array(val1,val2,val3)
$args = array( 'key1' , 'key2' , 'key3' , 'arraykey' ); $metas = get_post_meta_multiple( $args , $post->ID );
$argsに取得したいカスタムフィールドのキーを配列で入れてください。
1つの場合もarray( ‘key’ )として下さい。
$metasをpreするとこうなります。
stdClass Object ( [key1] => val1 [key2] => val2 [key3] => val3 [arraykey] => Array ( [0] => val1 [1] => val2 [2] => val3 ) ) //見つからないキーがあった場合はこうなります。 例)key4を設定してた場合 stdClass Object ( [key1] => val1 [key2] => val2 [key3] => val3 [arraykey] => Array ( [0] => val1 [1] => val2 [2] => val3 ) [key4] => )
出力は取得した変数->メタキーです。
echo $metas->key1 //結果 val1 foreach( $metas->arraykey as $val ) { echo $val . '<br>'; } //結果 val1 val2 val3
注意事項
カスタムフィールドは同じキー名で複数作成することがきます。
上記のコードは1キー1値が前提なので、その可能性がある場合は下記コードを使用して下さい。
使用するデータ
- key1 → val1
- key1 → val2
- key2 → val3
- arraykey → array(val1,val2,val3)
foreachの部分を書き換えて下さい。
foreach( $getmetas as $meta ) { //値がシリアライズされているか $value = ( is_serialized( $meta->meta_value ) ) ? unserialize( $meta->meta_value ) : $meta->meta_value ; //同じキーが複数ある場合配列で保存 if( isset( $metas[ $meta->meta_key ] ) ) { if( !is_array( $metas[ $meta->meta_key ] ) ) { $metas[ $meta->meta_key ] = (array)$metas[ $meta->meta_key ]; } $metas[ $meta->meta_key ][] = $value; } else { $metas[ $meta->meta_key ] = $value; } }
結果
stdClass Object ( [key1] => Array ( [0] => val1 [1] => val2 ) [key2] => val3 [arraykey] => Array ( [0] => val1 [1] => val2 [2] => val3 ) )
この場合、key1が1つか2つかわからないので出力するときに配列に変換してやる必要がありますね。
//これはエラーが出ます echo $metas->key1 //こうする foreach( (array)$metas->key1 as $val ) { echo $val . '<br>'; }
なら全部配列で入れときゃいいじゃん!ってことで
foreach( $getmetas as $meta ) { //値がシリアライズされているか $value = ( is_serialized( $meta->meta_value ) ) ? unserialize( $meta->meta_value ) : $meta->meta_value ; if( isset( $metas[ $meta->meta_key ] ) ) { $metas[ $meta->meta_key ][] = $value; } else { $metas[ $meta->meta_key ] = array( $value ); } }
出力結果
stdClass Object ( [key1] => Array ( [0] => val1 [1] => val3 ) [key2] => Array ( [0] => val2 ) [arraykey] => Array ( [0] => Array ( [0] => val1 [1] => val2 [2] => val3 ) ) )
いままでめんどくさいなーって思ってはいたけど改めて関数作るまでいってなかったんですよね。
わりかし使う場面あるかなーと思ったりもするので今後はこのコード使っていこうかと思います。
もしおかしいところあったら…すいません…
2019/12/15追記
get_post_custom()を使ったコード
カスタムフィールドを取得する際にキャッシュを使っているということがわかったので、単純にget_post_customを繰り返して取得するコードを作りました。
これだと配列云々考えなくても自動でやってくれるし楽ですね。
勉強不足でした…
$args = array( 'key1' , 'key2' , 'key3' , 'arraykey' ); $metas = array(); foreach( $args as $key ) { $metas[$key] = get_post_meta( $post->ID, $key, true ); }
出力結果
Array ( [key1] => val1 [key2] => val2 [key3] => val3 [arraykey] => Array ( [0] => val1 [1] => val2 [2] => val3 ) ) //出力 echo $metas['key1']; //結果 val1
extractという関数を使えば、キー名をそれぞれ変数名にして保管することが可能です。
extract($metas); //出力 echo $key1; //結果 val1