WordPress: множественное кастомное поле

Про полезность и сам принцип создания кастомных полей для постов в WordPress написано уже немало статей. Я бы хотел уделить внимание именно множественным полям. Можно ли записать массив данных в кастомное поле?

Оказывается можно! Причем об этом разработчики WordPress подумали и даже облегчили задачу. Вообще, чем больше знакомлюсь с этим движком, тем больше удивляюсь его внутренней красоте и продуманности!

Итак, задача. Требуется для постов записывать данные вида:

  • Параметр 1, Значение 1
  • Параметр 2, Значение 2
  • Параметр 3, Значение 3
  • и т.д., пусть и не до бесконечности, но, допустим, 50…100…500… параметров со своими значениями.

Как такое сделать? Естественно, что уж точно не следует создавать n-ое количество кастомных полей с соответствующими названиями (ключами). Это уж совсем изврат. Проще всего записывать такие данные массивом в одно поле.

Сразу скажу, что WordPress умеет работать со списками кастомных полей. Я имею в виду, что в кастомное поле с одним и тем же названием (ключом) можно записать несколько значений, в том числе несколько массивов. В последствии выводиться это будет в виде списка: моё_поле[0], моё_поле[1], моё_поле[2], и т.д. Соответственно это придётся учитывать потом в коде, проще говоря, возникают определённые заморочки. На мой взгляд, работать с кастомными полями-списками следует только в случае, когда предполагается записывать ну очень большие объемы данных.

Каждое кастомное поле в WordPress — это запись в базе данных в таблицу postmeta. При этом ячейка этой таблицы meta_value, куда будет непосредственно записываться значение кастомного поля, имеет тип «longtext». Если верить справочным данным, то для базы MySQL это означает возможность записать до 4294967300 байт или строки длиной до 4294967296 знаков. Согласитесь, что это очень дофига! Соответственно, даже если не записывать нужный нам массив в виде кастомного поля-списка, а записывать его именно в одно кастомное поле, то ограничения по объему мы достигнем очень-очень не скоро.

Множественное кастомное поле создаём с использованием файла functions.php из каталога темы сайта. Код следующий:

// Добавляем блок кастомного поля на страницу редактирования поста.
add_action('add_meta_boxes', 'cd_meta_box_add');
function cd_meta_box_add() {
  add_meta_box('post-datas', __('Post datas', 'd1mon'), 'cd_meta_box_post_datas', 'post', 'normal', 'high');
}

// Задаём максимальное число параметров массива.
$post_datas_max = 10;

// Формируем форму редактирования в блоке кастомного поля.
function cd_meta_box_post_datas($post)
{
  global $post_datas_max;
  $post_datas_i = 0;
  wp_nonce_field('my_meta_box_nonce', 'meta_box_nonce');

  if($post_datas = get_post_meta($post->ID, 'post_datas', true))
  {
    foreach($post_datas as $data_arr)
    {
      // Если есть значения, то заносим их в форму.
      if(mb_strlen($data_arr['name']) || mb_strlen($data_arr['value']))
      {
        $post_datas_i++;
        echo 'Name:<input name="data_name_'.$post_datas_i.'" id="data_name_'.$post_datas_i.'" value="'.esc_attr($data_arr['name']).'" /></p>';
        echo '<p>Value:<input name="data_value_'.$post_datas_i.'" id="data_value_'.$post_datas_i.'" value="'.esc_attr($data_arr['value']).'" /> ';
      }
    }
  }
  // Доформировываем форму пустыми полями.
  while($post_datas_i < $post_datas_max)
  {
    $post_datas_i++;
    echo 'Name:<input name="data_name_'.$post_datas_i.'" id="data_name_'.$post_datas_i.'" value="" /></p>';
    echo '<p>Value:<input name="data_value_'.$post_datas_i.'" id="data_value_'.$post_datas_i.'" value="" /> ';
  }
}

// Сохраняем кастомное поле при сохранении поста.
add_action( 'save_post', 'cd_meta_box_save' );
function cd_meta_box_save( $post_id )
{
  global $post_datas_max;
  $post_datas_i = 0;
  $post_datas_rec_i = 0;

  // Bail if we're doing an auto save
  if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;

  // if our nonce isn't there, or we can't verify it, bail
  if(!isset($_POST['meta_box_nonce']) || !wp_verify_nonce($_POST['meta_box_nonce'], 'my_meta_box_nonce')) return;

  // if our current user can't edit this post, bail
  if(!current_user_can('edit_post')) return;

  // Массив для фильтрации записываемых данных. В данном примере допустима запись тегов ссылок.
  $allowed = array('a' => array('href' => array()));

  // Собираем наш массив.
  while($post_datas_i <= $post_datas_max)
  {
    $post_datas_i++;
    if((isset($_POST['data_name_'.$post_datas_i]) && mb_strlen($_POST['data_name_'.$post_datas_i]))
    || (isset($_POST['data_value_'.$post_datas_i]) && mb_strlen($_POST['data_value_'.$post_datas_i])))
    {
      $post_datas_rec[$post_datas_rec_i]['name'] = isset($_POST['data_name_'.$post_datas_i]) && mb_strlen($_POST['data_name_'.$post_datas_i]) ? wp_kses($_POST['data_name_'.$post_datas_i], $allowed) : '';
      $post_datas_rec[$post_datas_rec_i]['value'] = isset($_POST['data_value_'.$post_datas_i]) && mb_strlen($_POST['data_value_'.$post_datas_i]) ? wp_kses($_POST['data_value_'.$post_datas_i], $allowed) : '';
      $post_datas_rec_i++;
    }
  }
  // Если массив собрался, то записываем.
  if(isset($post_datas_rec))
  {
    update_post_meta($post_id, 'post_datas', $post_datas_rec);
  }
}

Этот код делает основную работу. Он создает форму для редактирования массива на странице редактирования поста, который в последствии будет записываться в кастомное поле с именем «post_datas». Размер этого массива задаётся переменной $post_datas_max. В примере полей не много = 10шт. Они выводятся сразу в форму. Если полей будет больше, то имеет смысл уже заморочиться с AJAX, чтобы по умолчанию выводить только, допустим, 10 полей (ну или сколько уже содержат данные), а дополнительные добавлять в форму по клику «Добавить ещё».

Также этот код сохраняет данные формы при сохранении поста. Ну и производит другие действия, вроде фильтрации данных и защиты от записи формы лицами, которые не имеют на это прав.

Стоит отметить, что в коде нет каких-либо дополнительных преобразований, т.е. мы сформировали массив и сразу записали его в кастомное поле. На самом же деле, конечно, массив записывается в поле в преобразованном виде. Просто эту работу берет на себя функционал WordPress. Конкретно, функция update_post_meta, когда видит, что ей предлагают записать в кастомное поле не строку, а массив, то автоматически преобразует его с использованием PHP-функции serialize().

Обратное преобразование с использованием unserialize() тоже может производится автоматически. Для этого следует использовать функцию WordPress под названием get_post_meta(). Далее пример вывода нашего массива на странице. Код следует размещать в файле single.php.

if($post_datas = get_post_meta($post->ID, 'post_datas', true))
{
  $post_datas_html = '';
  //echo "<pre>",htmlspecialchars(print_r($post_datas,true)),"</pre>";

  foreach($post_datas as $post_datas_arr)
  {
    // Если есть инфа.
    if(!empty($post_datas_arr['name']) || !empty($post_datas_arr['value']))
    {
      if(!empty($post_datas_arr['name']))
      {
        $post_datas_html .= '<p>Name: '.$post_datas_arr['name'].'</p>';
      }
      if(!empty($post_datas_arr['value']))
      {
        $post_datas_html .= '<p>Value: '.$post_datas_arr['value'].'</p>';             }
    }
  }
  if(mb_strlen($post_datas_html))
  {
    ?><div class="post-data"><?php echo $post_datas_html; ?></div><?php
  }
}

Ну, вот и всё.

Я использовал данный способ для сохранения в массив ссылок, прикреплённых к посту, т.е. сохранял URL и TITLE для каждой ссылки. Но вообще, применений можно найти массу.

Множественное кастомное поле WordPress

4 Комментария

  1. Здравствуйте.
    Я не очень разбираюсь в коде, но вот такие вот поля очень мне бы пригодились.
    Подскажите какой нибудь плагин, что бы ничего лишнего.
    Только то что у Вас описано.
    Спасибо.

  2. Здравствуйте! Дмитрий, а скажите пожалуйста, как на таком же примере добавить дополнительно поле для вставки миниатюры ?

    • Нужно, чтобы для поста задавалась одна дополнительная миниатюра? Наверное можно сделать такое кастомное поле. Как бы, в статье речь не совсем о том, либо я не понял, что вы хотите.

Добавить комментарий