カスタム投稿で複数のメタボックスを動的に作成する方法

Wordpressのカスタム投稿画面において、1つの記事に対してN個のメタボックスを作成します。メタボックスには追加、更新、削除ボタンを用意し、それぞれ押下することでpostmetaテーブルへ値を反映します。
目的
メタボックスをデフォルトのカスタムフィールドのように、ボタンを押すことで動的に増やせるようにする。 1つのカスタム投稿に対して、複数のメタボックスを任意の数だけ作成、更新、削除ができる。
とりあえず動くものを目的に作成、想定外の入力値ケースなど、エラーハンドリングはとりあえずは今度考えようと思う。 前回カスタムフィールドとメタボックスの動き(値の操作について) の続き。
- デフォルトのカスタムフィールドのようにメタボックスを追加、更新、削除できる
 
結果
仕様概要
音楽イベント情報を紹介する記事を想定し、出演DJの名前、クレジットを任意の数だけ登録するメタボックスを使用できるカスタム投稿を作成する。 DJ名記載欄に対してクレジット記載欄は1対1。例えばDJ名:AAA、クレジット:BBBレコード、のようなイメージ。
- DBへの値の更新はWordpressの関数を使用する。(add_post_meta(),update_post_meta(),delete_post_meta())
 - WordPressの関数は、追加、更新、削除、各ボタンを押すことでAJAXを使用して外部PHPファイルから実行する。
 - AJAXはWordpressの「admin-ajax.php」を使用する。
 - postmetaテーブル内のmeta_keyは重複を許す。(add_post_meta()時に引数をfalseで与える)
 - 入力値は複数を想定し、JSON形式で格納する。update_post_meta()、delete_post_meta()時に必要な変更前の値は入力フィールドを非表示にして持つ。
 
カスタム投稿の作成方法はWordPressでカスタム投稿タイプとメタボックスを使用して独自の投稿画面を作成する方法で作成したものを使用する。
イメージ
「追加」ボタンを押すと入力値がDBへ反映され、入力フィールドが増える。
「更新」ボタンを押すとDBが更新される。
「削除」ボタンを押すとDBからレコードが削除され、入力フィールドが消える。
サンプルコード
▼function.php
/** ADD AJAX METABOX   **/
require ( dirname(__FILE__) . '/include/admin-meta-event-dynamic.php');
add_action('admin_menu', 'add_event_box_dynamic');
require ( dirname(__FILE__) . '/include/admin-ajax-postmeta.php');
▼admin-ajax-postmeta.php
<?php
function webmemo_ajax_add_post_meta() {
  check_ajax_referer('nonce_ajax','secure');
  add_post_meta($_REQUEST['postId'], $_REQUEST['metaKeys'], wp_slash($_REQUEST['metaVal']), false);
  die();
}
function webmemo_ajax_update_post_meta() {
  check_ajax_referer('nonce_ajax','secure');
  update_post_meta($_REQUEST['postId'], $_REQUEST['metaKeys'], wp_slash($_REQUEST['metaVal']), $_REQUEST['metaValBefore']);
  die();
}
function webmemo_ajax_delete_post_meta() {
  check_ajax_referer('nonce_ajax','secure');
  delete_post_meta($_REQUEST['postId'], $_REQUEST['metaKeys'], wp_slash($_REQUEST['metaValBefore']));
  die();
}
//ログインユーザーがactionする場合
//非ログインユーザーがactionする場合
add_action('wp_ajax_webmemo_ajax_add_post_meta','webmemo_ajax_add_post_meta');
add_action('wp_ajax_nopriv_webmemo_ajax_add_post_meta','webmemo_ajax_add_post_meta');
add_action('wp_ajax_webmemo_ajax_update_post_meta','webmemo_ajax_update_post_meta');
add_action('wp_ajax_nopriv_webmemo_ajax_update_post_meta','webmemo_ajax_update_post_meta');
add_action('wp_ajax_webmemo_ajax_delete_post_meta','webmemo_ajax_delete_post_meta');
add_action('wp_ajax_nopriv_webmemo_ajax_delete_post_meta','webmemo_ajax_delete_post_meta');
?>
▼admin-meta-event-dynamic.php
<?php
  function add_event_box_dynamic() {
    add_meta_box('event_info_dynamic', 'DJs', 'event_info_form_dynamic', 'event', 'normal', 'high');
  }
  function event_info_form_dynamic() {
?>
    <!--- HTML AREA --->
    <div id="add-fields-dj">
      <?php
        $djs =get_post_meta( get_the_ID() ,'_djs', false);
        $i = 0;
        foreach ( $djs as $dj ) {
          $tmp = json_decode(stripslashes($dj),false);
          echo '<div id="inputDjs-' . $i . '" class="djs"><table>';
          echo '<tr><th>DJ名</th><td><textarea name="inputDjs" style="width:400px;">'. $tmp->inputDjs .'</textarea></td></tr>';
          echo '<tr><th>クレジット</th><td><textarea name="inputCredit" style="width:400px;">' . $tmp->inputCredit . '</textarea></td></tr>';
          echo '<textarea style="display:none;" name="hiddenDjs">' . $tmp->inputDjs . '</textarea>';
          echo '<textarea style="display:none;" name="hiddenCredit">' . $tmp->inputCredit . '</textarea>';
          echo '</table>';
          echo '<button class="fields_update_btn">更新</button><button class="fields_delete_btn">削除</button>';
          echo '</div>';
          $i++;
        }
      ?>
    </div>
    <div id="inputBase" class="djs">
      <table>
        <tr><th>DJ名</th><td><textarea name="inputDjs" style="width:400px;"></textarea></td></tr>
        <tr><th>クレジット</th><td><textarea name="inputCredit" style="width:400px;"></textarea></td></tr>
        <tr><td colspan="2"><button id="fields_base" type="button" class="fields_add_btn">追加</button></td></tr>
      </table>
    </div>
    <script type='text/javascript'>
      ( function( $ ) {
        // Get AJAX File URL
        var admin_ajax_url  = '<?php echo admin_url('admin-ajax.php', __FILE__); ?>';
        var $addFieldsDj = $('#add-fields-dj');
        // Base Add BTN
        $(document).on("click", ".fields_add_btn", function (event) {
          event.preventDefault();
          $inputBase = $('#inputBase');
          $cloneElem = $inputBase.clone();
          // MAKE ARRAY
          var inputData = {};
          $cloneElem.find('textarea').map(function(index , element) {
            inputData[$(element).attr('name')] = $(element).val();
          });
          // ADD AJAX
          $.ajax({
            type : 'POST',
            url: admin_ajax_url,
            data: {
              'action': 'webmemo_ajax_add_post_meta',
              'secure': '<?php echo wp_create_nonce('nonce_ajax'); ?>',
              'postId': '<?php the_ID(); ?>',
              'metaKeys': '_djs',
              'metaVal': JSON.stringify(inputData),
            },
            success: function( response ){
              console.log(response);
              // ADD GUI
              $cloneElem.find('.fields_add_btn').remove();
              $cloneElem.removeAttr('id');
              $cloneElem.append('<button class="fields_update_btn">更新</button><button class="fields_delete_btn">削除</button>');
              $cloneElem.append('<textarea style="display:none;" name="hiddenDjs">' + $cloneElem.find('textarea[name="inputDjs"]').val() + '</textarea><textarea style="display:none;" name="hiddenCredit">' + $cloneElem.find('textarea[name="inputCredit"]').val() + '</textarea>');
              $cloneElem.appendTo('#add-fields-dj');
              $inputBase.find('textarea').map(function(index , element) {
                $(element).val("");
              });
            }
          });
          return false;
        });
        // UPDATE
        $(document).on("click", ".fields_update_btn", function (event) {
          event.preventDefault();
          var $parentDiv = $(this).parent();
          // MAKE ARRAY
          var inputData = {};
          $parentDiv.find('textarea[name^="input"]').map(function(index , element) {
            inputData[$(element).attr('name')] = $(element).val();
          });
          var baseData = {};
          $parentDiv.find('textarea[name^="hidden"]').map(function(index , element) {
            baseData[$(element).attr('name').replace('hidden','input')] = $(element).val();   // hidden を置換する
          });
          // UPDATE AJAX
          $.ajax({
            type : 'POST',
            url: admin_ajax_url,
            data: {
              'action': 'webmemo_ajax_update_post_meta',
              'secure': '<?php echo wp_create_nonce('nonce_ajax'); ?>',
              'postId': '<?php the_ID(); ?>',
              'metaKeys': '_djs',
              'metaVal': JSON.stringify(inputData),
              'metaValBefore': JSON.stringify(baseData),
            },
            success: function( response ){
              console.log(response);
              // update hidden
              $parentDiv.find('textarea[name="hiddenDjs"]').val($parentDiv.find('textarea[name="inputDjs"]').val());
              $parentDiv.find('textarea[name="hiddenCredit"]').val($parentDiv.find('textarea[name="inputCredit"]').val());
            }
          });
          alert("update");
          return false ;
        });
        // DELETE
        $(document).on("click", ".fields_delete_btn", function (event) {
          event.preventDefault();
          var $parentDiv = $(this).parent();
          // MAKE ARRAY
          var baseData = {};
          $(this).parent().find('textarea[name^="hidden"]').map(function(index , element) {
            baseData[$(element).attr('name').replace('hidden','input')] = $(element).val();   // hidden を置換する
          });
          // DELETE AJAX
          $.ajax({
            type : 'POST',
            url: admin_ajax_url,
            data: {
              'action': 'webmemo_ajax_delete_post_meta',
              'secure': '<?php echo wp_create_nonce('nonce_ajax'); ?>',
              'postId': '<?php the_ID(); ?>',
              'metaKeys': '_djs',
              'metaValBefore': JSON.stringify(baseData),
            },
            success: function( response ){
              console.log(response);
              // delete
              $parentDiv.remove();
            }
          });
          console.log(baseData);
          alert("delete");
          return false ;
        });
      } )( jQuery );
    </script>
<?php
  }
?>
方法
フロー
| 1外部ファイルの定義 | 
| ↓ | 
| 2AJAXで実行する関数を作成(DB値の更新部分(admin-ajax-postmeta.php)) | 
| ↓ | 
| 3管理画面の出力部分を作成(admin-meta-event-dynamic.php) | 
| ↓ | 
| 4データの追加部分作成(admin-meta-event-dynamic.php) | 
| ↓ | 
| 5データの更新部分作成(admin-meta-event-dynamic.php) | 
| ↓ | 
| 6データの削除部分作成(admin-meta-event-dynamic.php) | 
今回編集または、新規追加するファイル
テーマディレクトリ
   ├ function.php                     <--- 外部ファイルの読み込み
   └ include(dir)
     ├ admin-ajax-postmeta.php        <--- DB更新用Wordpress関数を実行している関数が記載されたファイル
     └ admin-meta-event-dynamic.php   <--- GUIの表示やJavascriptが埋め込まれたファイル
外部ファイルの定義(function.php)
通常のメタボックスを作成する際は値の保存をsave_postにフックした関数を作成し実行したが、今回はAJAXでボタンが押されたタイミングで値を保存する関数を動かすため、 save_postへのフックは行わない。変わりにajaxを使用する関数をadd_actionするが、外部ファイル(admin-ajax-postmeta.php)内に記述した。
▼function.php
/** ADD AJAX METABOX   **/
require ( dirname(__FILE__) . '/include/admin-meta-event-dynamic.php');
add_action('admin_menu', 'add_event_box_dynamic');
require ( dirname(__FILE__) . '/include/admin-ajax-postmeta.php');
AJAXで実行する関数を作成(DB値の更新部分(admin-ajax-postmeta.php))
メタボックスの値をDBに反映させる方法はWordpressの関数add_post_meta(),update_post_meta(),delete_post_meta()を使用した。
▼admin-ajax-postmeta.php
文字列にエスケープ文字などが入っている場合、update_post_meta()、delete_post_meta()で使用する変更前の値を、上手く同じ値と認識してくれない。 add_post_meta()時にエスケープ文字の処理を考慮して値を格納したほうがよいと考え、 JSONのエスケープ文字を格納するためwp_slash()でエスケープを1レベル増やす update_post_meta()の第4引数はなぜかwp_slash()をつけると文字列が合致しないらしく失敗した。delete_post_meta()はなぜかwp_slash()をつけないと失敗する。 同じ文字列比較していると思っていたが、動きが異なる
add_post_meta($_REQUEST['postId'], $_REQUEST['metaKeys'], stripslashes($_REQUEST['metaVal']), false);
function webmemo_ajax_add_post_meta() {
  check_ajax_referer('nonce_ajax','secure');
  add_post_meta($_REQUEST['postId'], $_REQUEST['metaKeys'], wp_slash($_REQUEST['metaVal']), false);
  die();
}
function webmemo_ajax_update_post_meta() {
  check_ajax_referer('nonce_ajax','secure');
  update_post_meta($_REQUEST['postId'], $_REQUEST['metaKeys'], wp_slash($_REQUEST['metaVal']), $_REQUEST['metaValBefore']);
  die();
}
function webmemo_ajax_delete_post_meta() {
  check_ajax_referer('nonce_ajax','secure');
  delete_post_meta($_REQUEST['postId'], $_REQUEST['metaKeys'], wp_slash($_REQUEST['metaValBefore']));
  die();
}
各postmetaテーブルへ値を格納するための関数を登録する。 非ログインユーザについては、今回の目的として必要性ではないけど覚書きとして記載する。
//ログインユーザーがactionする場合
//非ログインユーザーがactionする場合
add_action('wp_ajax_webmemo_ajax_add_post_meta','webmemo_ajax_add_post_meta');
add_action('wp_ajax_nopriv_webmemo_ajax_add_post_meta','webmemo_ajax_add_post_meta');
add_action('wp_ajax_webmemo_ajax_update_post_meta','webmemo_ajax_update_post_meta');
add_action('wp_ajax_nopriv_webmemo_ajax_update_post_meta','webmemo_ajax_update_post_meta');
add_action('wp_ajax_webmemo_ajax_delete_post_meta','webmemo_ajax_delete_post_meta');
add_action('wp_ajax_nopriv_webmemo_ajax_delete_post_meta','webmemo_ajax_delete_post_meta');
登録するadd_actionは、wp_ajax_以降を関数名と同じにしないとbad request(400)が返ってくる。(環境依存?) 使う関数名がwebmemo_ajax_add_post_meta()の場合
wp_ajax_${関数名}
wp_ajax_nopriv_${関数名}
管理画面の出力部分を作成(admin-meta-event-dynamic.php)
▼admin-meta-event-dynamic.php
関数の登録と呼び出し
function add_event_box_dynamic() {
  add_meta_box('event_info_dynamic', 'DJs', 'event_info_form_dynamic', 'event', 'normal', 'high');
}
function event_info_form_dynamic() {
メタボックスの表示
get_post_meta()でポストIDにひもづいた同じmeta_keyの値を配列で取得する。 update_post_meta()は同じmeta_keyが存在する場合の判別要素として、更新前の情報を必要とするため、非表示の要素として同じ値を保持しておく dalete_post_meta()においても、入力値が変更された後に削除ボタンを押されることを想定して、この非表示要素の値を使用する。 非表示のフィールドはname属性をhidden.*となる文字列で作成し、表示しているフィールドはname属性をinput.*となるように作成している。
add_post_meta()使用時にwp_slash()で増やしたエスケープを1レベル減らすためにstripslashes()を使用する。
<div id="add-fields-dj">
  <?php
    $djs =get_post_meta( get_the_ID() ,'_djs', false);
    $i = 0;
    foreach ( $djs as $dj ) {
      $tmp = json_decode(stripslashes($dj),false);
      echo '<div id="inputDjs-' . $i . '" class="djs"><table>';
      echo '<tr><th>DJ名</th><td><textarea name="inputDjs" style="width:400px;">'. $tmp->inputDjs .'</textarea></td></tr>';
      echo '<tr><th>クレジット</th><td><textarea name="inputCredit" style="width:400px;">' . $tmp->inputCredit . '</textarea></td></tr>';
      echo '<textarea style="display:none;" name="hiddenDjs">' . $tmp->inputDjs . '</textarea>';
      echo '<textarea style="display:none;" name="hiddenCredit">' . $tmp->inputCredit . '</textarea>';
      echo '</table>';
      echo '<button class="fields_update_btn">更新</button><button class="fields_delete_btn">削除</button>';
      echo '</div>';
      $i++;
    }
  ?>
</div>
メタボックス追加箇所の作成
<div id="inputBase" class="djs">
  <table>
    <tr><th>DJ名</th><td><textarea name="inputDjs" style="width:400px;"></textarea></td></tr>
    <tr><th>クレジット</th><td><textarea name="inputCredit" style="width:400px;"></textarea></td></tr>
    <tr><td colspan="2"><button id="fields_base" type="button" class="fields_add_btn">追加</button></td></tr>
  </table>
</div>
データの追加部分作成
あらかじめadmin-ajax.phpのファイルの場所を取得しておく
<script type='text/javascript'>
  ( function( $ ) {
    // Get AJAX File URL
    var admin_ajax_url  = '<?php echo admin_url('admin-ajax.php', __FILE__); ?>';
追加ボタンを押すと、textareaの値からDBへ格納するデータを作成し、AJAXを使用して実行しています。 入力フィールドをclone()で複製し、AJAX処理が成功した場合、複製された入力フィールドのIDの削除、追加ボタンの削除、 更新、削除ボタンの追加、変更前データ格納用の非表示にするフィールドを追加し、管理画面に追加します。
$(document).on("click", ".fields_add_btn", function (event) {
  event.preventDefault();  //ボタンの遷移をキャンセルする
  $inputBase = $('#inputBase');
  $cloneElem = $inputBase.clone();
  // DBへ格納する値を作成
  var inputData = {};
  $cloneElem.find('textarea').map(function(index , element) {
    inputData[$(element).attr('name')] = $(element).val();         // textareaのname属性と値で連想配列を作成する
  });
  // ADD AJAX
  $.ajax({
    type : 'POST',
    url: admin_ajax_url,                                           // admin-ajax.phpへリクエストを送る
    data: {
      'action': 'webmemo_ajax_add_post_meta',                      // WordPressに登録した関数名を記載する
      'secure': '<?php echo wp_create_nonce('nonce_ajax'); ?>',    // nonceを作成する()
      'postId': '<?php the_ID(); ?>',                              // 現在のポストIDを取得する
      'metaKeys': '_djs',
      'metaVal': JSON.stringify(inputData),                        // 複数の値を1レコードに収めるためJSON文字列にする
    },
    success: function( response ){
      console.log(response);
      // AJAX処理成功後、複製された値に対する処理、IDの削除、各ボタンの追加など
      $cloneElem.find('.fields_add_btn').remove();
      $cloneElem.removeAttr('id');
      $cloneElem.append('<button class="fields_update_btn">更新</button><button class="fields_delete_btn">削除</button>');
      $cloneElem.append('<textarea style="display:none;" name="hiddenDjs">' + $cloneElem.find('textarea[name="inputDjs"]').val() + '</textarea><textarea style="display:none;" name="hiddenCredit">' + $cloneElem.find('textarea[name="inputCredit"]').val() + '</textarea>');
      $cloneElem.appendTo('#add-fields-dj');
      $inputBase.find('textarea').map(function(index , element) {
        $(element).val("");
      });
    }
  });
  return false;
});
データの更新部分作成
データの更新時は、add_meta_post()の引数によって、重複したmeta_keyを許しているため一意にDBのレコードを識別するために変更後の値、変更前の値を取得します。 AJAX処理が成功した場合は、変更前データ格納用のフィールドを変更後のデータに書き換えています。(続けて更新ボタン押された場合を想定)
変更前、変更後の値はそれぞれを区別するためにname属性をフィルタリングして取得しています。 表示されているフィールドはtextarea[name^="input"](先頭がinputのnameを持つtextareaを取得する) 非表示されているフィールドはtextarea[name^="hidden"](先頭がhiddenのnameを持つtextareaを取得する) また、DBに格納されている値に合わせるために、非表示フィールドは文字列hiddenをinputに置換してPOSTします。
// UPDATE
$(document).on("click", ".fields_update_btn", function (event) {
  event.preventDefault();
  var $parentDiv = $(this).parent();
  // MAKE ARRAY
  var inputData = {};
  $parentDiv.find('textarea[name^="input"]').map(function(index , element) {          // 表示フィールドを取得し、DB用の値を作成する
    inputData[$(element).attr('name')] = $(element).val();
  });
  var baseData = {};
  $parentDiv.find('textarea[name^="hidden"]').map(function(index , element) {         // 非表示フィールドを取得し、DB用の値を作成する
    baseData[$(element).attr('name').replace('hidden','input')] = $(element).val();   // name属性のhiddenをinputに置換する
  });
  // UPDATE AJAX
  $.ajax({
    type : 'POST',
    url: admin_ajax_url,
    data: {
      'action': 'webmemo_ajax_update_post_meta',
      'secure': '<?php echo wp_create_nonce('nonce_ajax'); ?>',
      'postId': '<?php the_ID(); ?>',              // 現在のポストIDを取得する
      'metaKeys': '_djs',
      'metaVal': JSON.stringify(inputData),
      'metaValBefore': JSON.stringify(baseData),   // 重複したmeta_keyを許しているため更新前のデータも必要となる
    },
    success: function( response ){
      console.log(response);
      // update hidden
      $parentDiv.find('textarea[name="hiddenDjs"]').val($parentDiv.find('textarea[name="inputDjs"]').val());         //非表示フィールドの更新
      $parentDiv.find('textarea[name="hiddenCredit"]').val($parentDiv.find('textarea[name="inputCredit"]').val());   //非表示フィールドの更新
    }
  });
  alert("update");
  return false ;
});
        
データの削除部分作成
$(document).on("click", ".fields_delete_btn", function (event) {
  event.preventDefault();
  var $parentDiv = $(this).parent();
  // MAKE ARRAY
  var baseData = {};
  $(this).parent().find('textarea[name^="hidden"]').map(function(index , element) {   // 非表示フィールドを取得し、DB用の値を作成する
    baseData[$(element).attr('name').replace('hidden','input')] = $(element).val();   // name属性のhiddenをinputに置換する
  });
  // DELETE AJAX
  $.ajax({
    type : 'POST',
    url: admin_ajax_url,
    data: {
      'action': 'webmemo_ajax_delete_post_meta',
      'secure': '<?php echo wp_create_nonce('nonce_ajax'); ?>',
      'postId': '<?php the_ID(); ?>',            // 現在のポストIDを取得する
      'metaKeys': '_djs',
      'metaValBefore': JSON.stringify(baseData), // 非表示フィールドの値を使用する(表示されているフィールドは書きかえられているかもしれないため)
    },
    success: function( response ){
      console.log(response);
      // delete
      $parentDiv.remove(); // AJAXが成功したらGUIからフィールドを削除する
    }
  });
  console.log(baseData);
  alert("delete");
  return false ;
});
今後、入れられた値がNULLだった場合や、同じ値が入っている場合等々、まだ考えることはたくさんありそうですが、 とりあえずまず、動くものとしてはこんな感じになった。
▼add_post_meta()
| 値 | 説明 | 
|---|---|
| $post_id | (整数) (必須) カスタムフィールドを追加する投稿の ID。 | 
| $meta_key | (文字列) (必須) カスタムフィールドのキー。 | 
| $meta_value | (mixed) (必須) カスタムフィールドの値。配列を与えるとシリアライズされて文字列が格納されます。 | 
| $unique | (真偽値) (オプション) キーをユニークにするかどうか。true のときは、指定した投稿に $meta_key を持つカスタムフィールドが無いときのみ追加し、すでにカスタムフィールドが存在していれば追加されません。 | 
| 戻り値 | (真偽値|整数) 成功すると、追加された行(カスタムフィールド)の ID を返します。これは true として判定できます。$unique 引数が true で、指定されたキーを持つカスタムフィールドが既にあると、false を返します。 | 
▼update_post_meta()
| 値 | 説明 | 
|---|---|
| $post_id | (整数) (必須) 更新したいカスタムフィールドを持つ投稿の ID。 | 
| $meta_key | (文字列) (必須) 更新したいカスタムフィールドのキー。 | 
| $meta_value | (mixed) (必須) カスタムフィールドの新しい値。配列を与えるとシリアル化されます。 | 
| $prev_value | (mixed) (オプション) 更新したいカスタムフィールドの元の値。これは、同じキーを持つカスタムフィールドを区別するパラメータです。省略すると、指定したキーを持つカスタムフィールドはすべて値が更新されます。 | 
| 戻り値 | (mixed)カスタムフィールドが存在しなければ meta_id を返します(true と判定できます)。それ以外の場合、成功すれば true、失敗すれば false を返します。また、与えられた値がすでにデータベースにある値と一致したときも false を返します。 | 
▼delete_post_meta()
| 値 | 説明 | 
|---|---|
| $post_id | (整数)(必須) 削除したいカスタムフィールドを持つ投稿の ID | 
| $meta_key | (文字列)(必須) 削除したいカスタムフィールドのキー。 | 
| $meta_value | (mixed)(オプション) 削除したいカスタムフィールドの値。これは、同じキーを持つカスタムフィールドを区別するパラメータです。省略すると、指定したキーを持つカスタムフィールドはすべて削除されます。 | 
| 戻り値 | (真偽値) 失敗なら False、成功なら True を返す。 | 
参考
- stripslashes() -PHP-
 - delete_post_meta() -WORDPRESS CODEX-
 - add_post_meta() -WORDPRESS CODEX-
 - update_post_meta() -WORDPRESS CODEX-
 
その他詳細
DBへ格納される値
DJ名とクレジットを1セットと考え格納したかったため、1つのレコードに複数の値を入れることにした。 複数の値がそれぞれ何の情報かわかるように、それぞれ入力フィールドのname属性と値という連想配列のようなものを作成している。
DJ名:inputDjs
クレジット:inputCredit
追加前
mysql> select * from wp_XXXXpostmeta WHERE post_id = 40;
+---------+---------+--------------------+--------------------------------+
| meta_id | post_id | meta_key           | meta_value                     |
+---------+---------+--------------------+--------------------------------+
|      14 |      40 | _edit_last         | 1                              |
|      15 |      40 | _edit_lock         | 1563632463:1                   |
|      16 |      40 | _event-description | サンプルメタボックス           |
|      17 |      40 | _event-date        | 2019-04-30                     |
+---------+---------+--------------------+--------------------------------+
4 rows in set (0.00 sec)
追加後
mysql> select * from wp_XXXXpostmeta WHERE post_id = 40;
+---------+---------+--------------------+-------------------------------------------------------------------------+
| meta_id | post_id | meta_key           | meta_value                                                              |
+---------+---------+--------------------+-------------------------------------------------------------------------+
|      14 |      40 | _edit_last         | 1                                                                       |
|      15 |      40 | _edit_lock         | 1563735477:1                                                            |
|      16 |      40 | _event-description | サンプルメタボックス                                                    |
|      17 |      40 | _event-date        | 2019-04-30                                                              |
|     233 |      40 | _djs               | {"inputDjs":"DJ さいとう","inputCredit":"さいとうレコード"}             |
+---------+---------+--------------------+-------------------------------------------------------------------------+
5 rows in set (0.00 sec)
add_post_meta()の第4引数をfalseにすると重複したmeta_keyを許す。
add_post_meta($_REQUEST['postId'], $_REQUEST['metaKeys'], wp_slash($_REQUEST['metaVal']), false);
mysql> select * from wp_XXXXpostmeta WHERE post_id = 40;
+---------+---------+--------------------+-------------------------------------------------------------------------+
| meta_id | post_id | meta_key           | meta_value                                                              |
+---------+---------+--------------------+-------------------------------------------------------------------------+
|      14 |      40 | _edit_last         | 1                                                                       |
|      15 |      40 | _edit_lock         | 1563735477:1                                                            |
|      16 |      40 | _event-description | サンプルメタボックス                                                    |
|      17 |      40 | _event-date        | 2019-04-30                                                              |
|     233 |      40 | _djs               | {"inputDjs":"DJ さいとう","inputCredit":"さいとうレコード"}             |
|     234 |      40 | _djs               | {"inputDjs":"トム","inputCredit":"バスター"}                            |
+---------+---------+--------------------+-------------------------------------------------------------------------+
6 rows in set (0.00 sec)
問題点
入力値を簡単にまとめて取得できなかった。
更新、削除に変更前(DBに入っている)の値が必要であり 入力値のまとまりをたくさん作る場合、たくさんのhiddenプロパティの入力を作って、form.serialize()でまとめてやろうと思いきや、 Wordpressの管理画面のそもそものformの中なのでformネストされた形になってしまい、うまく狙ったデータを取得できなかった。 少し考えたがあまりよい仕様が思い浮かばず、そもそもは目的はとりあえず動くものを作ってみたく、効率の良さや使いやすさは次のステップと考えていたため、 とりあえずベタ書きで行ってみる。
変更前、変更後の値の保存をベタ書きしてしまった。
更新時には文字列の変更前後の値が必要となる。削除時には変更前の値が必要となる。 値を変更したが削除ボタンを押されることを想定すると、変更前の値は必須であり、表示されないtextareaを作成して対応した。