[WordPress] カスタムフィールドで一つのキーに複数の値を保存

公開

スポンサーリンク

カスタムフィールドで一つのキーに一つの値を保存する方法はたくさんの解説があるのですが、プラグインなしで一つのキーに複数の値を保存・編集できるようにする方法はあまり説明されていないようです。

最近、1キーに複数の値を保存するカスタムフィールドを、自分でfunction.phpにコードを書いて用意したのですが、思わぬところでつまづいて時間をとられてしまいました。いろいろと勉強になったのと、役に立つこともあるかと思うので、備忘録を残しておきます。

投稿ページに表示するフォーム

まず投稿ページにカスタムフィールドの入力画面を表示するコードをfunction.phpに書きます。この部分は一つのキーに一つの値を保存するケースとあまり変わりません。気をつけるのは、nameの後ろに”[]“をつけて、値を配列で受け取れるようにすることです。

//投稿ページにカスタムフィールド入力欄を表示するアクションフック
add_action('admin_menu', 'add_my_custom_field');

//入力欄を表示する関数と、投稿タイプの指定
function add_my_custom_field() {
  add_meta_box('custom_field_industry', '担当楽器', 'show_my_custom_field', 'post', 'side' );
}

//投稿画面に表示される内容
function show_my_custom_field(){
  global $post;
  $forms = "";
  $values = get_post_meta($post->ID, 'my_custom_key_name', false);
  if ( !$values )
    $forms .= "<input type='text' name='my_custom_key_name[]'>";
  else
    foreach ($values as $val)
      $forms .= "<input type='text' name='my_custom_key_name[]' value='".$val."'><br>";
  
  $forms .=<<< END
  <a id='add_new_my_custom_field'>入力欄追加</a>
  <script>
    jQuery(function($){
      $("#add_new_my_custom_field").click(function(){
        $(this).prev().append("<li>{$field}</li>");
      });
    });
  </script>
END;
  echo $forms;
}

通常は入力欄は一つしか用意せず、必要になればjQueryで欄を増やすようにしてあります。このあたりは必要に応じて作り変えれば良いでしょう。

値を消去したい場合の対応で削除ボタンなどは設けず、入力欄を空欄にして保存すれば消えるようにしてあります。

カスタムフィールドの保存・更新・削除

以下が実際の保存や更新を行うアクションフックと関数群です。こちらもfunction.phpに書きます。

//カスタムフィールドの更新・保存のためのアクションフック
add_action('save_post', 'save_my_custom_field');

//カスタムフィールドの更新・保存を行う関数
function save_my_custom_field ($post_id) {
  //自動保存時は保存させない
  if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) 
    return $post_id;
  
  //最終リビジョンのpost_IDが入っている場合があるので、wp_is_post_revision()で本来のpost_IDを求める
  $post_id = wp_is_post_revision($post_id);
  
  //値がないなら保存されているキーを全て消去
  if ( !isset($_POST['my_custom_key_name']) || !$_POST['my_custom_key_name'] ) {
    return delete_all_custom_field_value ($post_id, "my_custom_key_name");
  } else {
    //値チェック 全項目がカラ文字列のケースをチェック
    $values = array();
    foreach ($_POST['my_custom_key_name'] as $val)
      if ( strlen($val) )
        $values[] = trim($val);
    
    //全部カラなら保存されているキーを全て消去
    if ( !$values )
      return delete_all_custom_field_value ($post_id, "my_custom_key_name");
    
    //重複を削除
    $values = array_unique($values);
    
    //保存済み項目と新項目を比較し、新項目から失われている既存項目を削除
    custom_field_del_old ($post_id, "my_custom_key_name", $values);
  }
}

//$field_nameで指定したカスタムフィールドキーで、$post_idに紐付けられたものを全て消去
function delete_all_custom_field_value ($post_id, $field_name) {
  delete_post_meta($post_id, $field_name);
  return $post_id;
}

//保存済みの値と新たに保存する値を比較し、新たに保存する値の中に存在しない保存済みの値を削除
//新たに保存する値のうち、保存済みの値にないものを保存
function custom_field_del_old ($post_id, $field_name, $news) {
  //保存済みの値を全て得る
  $tempo = get_post_meta($post_id, $field_name);
  
  $saved = !is_array($tempo) ? array($tempo):$tempo;
  
  //既保存で、新たに保存する値に存在しないものを消去
  foreach($saved as $val )
    if(!in_array($val, $news) )
      delete_post_meta($post_id, $field_name, $val);
  
  //新たに保存する値で、まだ保存されていないものを保存
  foreach($news as $n)
    if( !in_array($n, $saved) )
      add_post_meta($post_id, $field_name, $n);
  
  return $post_id;
}

6行目のif ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )は自動保存の時はカスタムフィールドの保存を行わない処理です。自動保存にも対応させたい場合はこの部分を取っ払ってください。

11行目のwp_is_post_revision()は非常に重要な部分です。投稿更新のタイミングにフックしてアクションを登録した時に、関数に渡される引数のpost_idは、公開される投稿post_idではなく、最終リビジョンのpost_idの場合があるのです。

カスタムフィールドの値は公開される記事のpost_idで記録されていますので、そのままではこの後に行う保存・更新・削除で不具合が出てきます。そこで、リビジョンのpost_idを渡すと、公開post_idを返してくれる関数のwp_is_post_revision()で本来のpost_idを取得します。

13行目から25行目は、カスタムフィールドの値をすべて消去する場合の処理です。実際の全削除は36~38行目の関数で行っています。

31行目で保存済みの値と新たに保存したい値を比べる関数に処理を渡しています。43行目以降の関数がそれです。全削除と更新を別の関数に切り分けているのは、他のカスタムフィールドからも使えるようにするためです。

43行目からの更新関数では、保存済みの値から不要になったものだけを削除し、新たに保存する必要がない値は更新を行わないようにして、最小限の動作で済むようにしてあります。

スポンサーリンク


Comment