Все о пользовательских типах записей WordPress

Здравствуйте, уважаемые читатели XoZbloga! Система управления сайтом WordPress завоевала признание в течении нескольких лет, но настоящим прорывом стала реализация возможности разделять записи на типы. В этом уроке подробно рассмотрим пользовательские типы записей их создание и использование.

Немного истории

На практике, пользовательские типы записей появились достаточно давно, а точнее с 17 февраля 2005 года, когда в WordPress 1.5 была добавлена ​​поддержка пользовательских типов для статических страниц, посредством post_type поля в базе данных. Функция Wp_insert_post() была примерно с WordPress 1.0 так что, когда поле post_type было реализовано в 1.5, можно было достаточно просто заполнить его с помощью этой функции.

И только в версии 2.8 появилась функция register_post_type() для создания пользовательских типов и некоторые другие полезные вещи были доступны в «ночных сборках», а уже с 2.9 функции стали доступны всем.


Пользовательские типы записей в WordPress

А что сейчас?!

Пользовательский тип записи это не более чем обычная запись (статья) с определенным значением поля post_type в базе данных. В обычной записи поле post_type имеет значение post, страница имеет значение page и так далее. Однако теперь мы можем создавать свои собственные типы, чтобы указать особенности контента, содержащегося в записи. Можно создать пользовательские типы записей для книг, фильмов, анекдотов, продуктов и всего чего угодно.
Если все сделано правильно, то с помощью всего лишь нескольких строк кода можно достичь следующих результатов:

  • Вынести пользовательский тип на основную панель админ зоны с пунктами подменю: список всех записей с таким типов, а также создать новую запись с таким типом;
  • Создать архив записей подобного типа, то есть сделать что-то на подобии главной страницы для пользовательского типа;
  • Создать категории и теги, которые могут быть доступными для пользовательских типов записей, а также пользовательские таксономии.

Различные типы контента предъявляют различные требования к данным. Для обычных записей вы хотите, чтобы был указан автор, категория и дата. В то время как для записи с типом «книги», хотелось бы иметь возможность указать автора книги, количество страниц, жанр, издательство и другие конкретные данные. Этого легко добиться используя пользовательские (meta boxes) области для ввода данных.

Meta boxes — области для ввода дополнительных данных прямо на странице создания записи. Такие области упрощают работу с пользовательскими типами записей.


Meta boxes - область для ввода дополнительных данных

Работа с пользовательскими типами записей

Чтобы эффективно создавать и использовать пользовательские типы записей, Вы должны быть знакомы со следующими составляющими:

  • Создание пользовательских типов записей;
  • Создание пользовательской таксономии;
  • Создание пользовательских областей данных.

Создание пользовательских типов записей

Первое что необходимо сделать, так это создать непосредственно сам пользовательский тип записи. В идеале нужно создать плагин для работы с пользовательскими типами записей, но Вы также можете использовать файл functions.php вашей темы.

1
2
3
4
5
function my_custom_post_product() {
   $args = array();
   register_post_type( 'product', $args );   
}
add_action( 'init', 'my_custom_post_product' );

Это простейшая форма создания типа, который практически не имеет настроек. Для разработки нового типа наших записей, будем использовать некоторые из наиболее часто используемых опций и добавим их к ранее пустому массиву $args.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function my_custom_post_product() {
   $labels = array(
      'name'               => _x( 'Продукция', 'post type general name' ),
      'singular_name'      => _x( 'Продукт', 'post type singular name' ),
      'add_new'            => _x( 'Добавить новый', 'product' ),
      'add_new_item'       => __( 'Добавить новый продукт' ),
      'edit_item'          => __( 'Редактировать продукт' ),
      'new_item'           => __( 'Новый продукт' ),
      'all_items'          => __( 'Вся продукция' ),
      'view_item'          => __( 'Смотреть продукт' ),
      'search_items'       => __( 'Найти продукт' ),
      'not_found'          => __( 'Продукты не найдены' ),
      'not_found_in_trash' => __( 'Нет удаленной продукции' ),
      'parent_item_colon'  => '',
      'menu_name'          => 'Продукция'
   );
   $args = array(
      'labels'        => $labels,
      'description'   => 'Пользовательский тип записей продукции',
      'public'        => true,
      'menu_position' => 5,
      'supports'      => array( 'title', 'editor', 'thumbnail', 'excerpt', 'comments', 'product_category'),
      'has_archive'   => true,
   );
   register_post_type( 'product', $args );   
}
add_action( 'init', 'my_custom_post_product' );

Итак мы добавили несколько опций в массив $args:

  • labels — данный массив меток используется для описания создаваемого пользовательского типа записи в теме./li>
  • description — краткая информация создаваемого пользовательского типа записи, что он делает и почему мы его используем.
  • public — использовать ли пользовательский тип публично и показывать ли его в административной зоне. В данном случае установлено истина.
  • menu_position — позиция пункта меню нашего типа на основной панели администратора. Значение 5 значит пункт установиться сразу после пункта меню «Записи», если 10 значит после пункта «Медиафайлы» и тд.
  • supports — данная опция содержит массив, в котором описаны те поля которые мы можем редактировать на странице создания записи. То есть title — появится поле для ввода названия записи, editor — будет отображена текстовая область для ввода текста записи и тд. А также указана используемая пользовательская таксономия product_category.
  • has_archive — если установлено true, будет создано правило rewrite, позволяя получить список записей нашего типа по адресу http://mysite.com/product/


Созданный пользовательский тип записей

После установки этого кода в файл function.php, вы должны увидеть меню для пользовательского типа записей. Появилась возможность добавлять запись и просмотреть список записей.

Однако, это далеко не все опции для массива аргументов, полный список опций и все возможности пользовательских типов Вы найдете здесь.

Интерактивные оповещения

WordPress генерирует некоторые сообщения, вызванные действиями пользователя. Мы также можем создать подобные сообщения, чтобы оповестить пользователя при работе с типами. Делается это с помощью хака post_updated_messages.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function my_updated_messages( $messages ) {
   global $post, $post_ID;
   $messages['product'] = array(
      0 => '',
      1 => sprintf( __('Продукт обновлен. <a href="%s">Посмотреть</a>'), esc_url( get_permalink($post_ID) ) ),
      2 => __('Пользовательские поля обновлены.'),
      3 => __('Пользовательские поля обновлены.'),
      4 => __('Продукт обновлен.'),
      5 => isset($_GET['revision']) ? sprintf( __('Product restored to revision from %s'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
      6 => sprintf( __('Продукт опубликован. <a href="%s">Посмотреть</a>'), esc_url( get_permalink($post_ID) ) ),
      7 => __('Продукт сохранен.'),
      8 => sprintf( __('Продукт отправлен. <a target="_blank" href="%s">Посмотреть</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
      9 => sprintf( __('Продукт запланирован на: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Посмотреть</a>'), date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
      10 => sprintf( __('Product draft updated. <a target="_blank" href="%s">Посмотреть</a>'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
   );
   return $messages;
}
add_filter( 'post_updated_messages', 'my_updated_messages' );

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


Интерактивное оповещение пользователей

Контекстные подсказки

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function my_contextual_help( $contextual_help, $screen_id, $screen ) {
   if ( 'edit-product' == $screen->id ) {

      $contextual_help = '<h2>Продукция</h2>
      <p>На этой странице находится список всей продукции, которая продается на сайте. Записи расположены в обратном хронологическом порядке, последними в списке являются товары, которые мы добавили первыми.</p>
      <p>Вы можете просматривать / редактировать сведения о каждом продукте, нажав на его название, или можете выполнить массовые действие с помощью выпадающего меню, выбрав несколько элементов.</p>'
;

   } elseif ( 'product' == $screen->id ) {

      $contextual_help = '<h2>Создание/редактирование продукта</h2>
      <p>Эта страница позволяет создать продукт или отредактировать уже имеющиеся данные о нем. Пожалуйста, не забудьте заполнить  дополнительные поля.</p>'
;

   }
   return $contextual_help;
}
add_action( 'contextual_help', 'my_contextual_help', 10, 3 );

Для того чтобы показать такую подсказку нам необходимо знать идентификатор экрана. Если при создании понадобится узнать ID экрана просто делаем так:

1
echo $screen->id;


Контекстные подсказки

Пользовательская таксономия

На обычного блога для создания организационной структуры вполне хватает категорий и тегов. В то время как для пользовательских типов постов этого не всегда достаточно. Такую проблему может решить пользовательская таксономия. В нашем случае таксономия с названием «Категории продуктов», которая используется только для записей с пользовательским типов «Продукция».

Процесс создания пользовательской таксономии практически идентичен созданию пользовательских типов записей. Давайте посмотрим на нашем примере:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function my_taxonomies_product() {
   $labels = array(
      'name'              => _x( 'Категории продуктов', 'taxonomy general name' ),
      'singular_name'     => _x( 'Категория продуктов', 'taxonomy singular name' ),
      'search_items'      => __( 'Найти категорию продуктов' ),
      'all_items'         => __( 'Все категории продуктов' ),
      'parent_item'       => __( 'Родительская категория продуктов' ),
      'parent_item_colon' => __( 'Родительская категория продуктов:' ),
      'edit_item'         => __( 'Редактировать категорию продуктов' ),
      'update_item'       => __( 'Обновить категорию продуктов' ),
      'add_new_item'      => __( 'Добавить новую категорию продуктов' ),
      'new_item_name'     => __( 'Новая категория продуктов' ),
      'menu_name'         => __( 'Категории продуктов' ),
   );
   $args = array(
      'labels' => $labels,
      'hierarchical' => true,
   );
   register_taxonomy( 'product_category', 'product', $args );
}
add_action( 'init', 'my_taxonomies_product', 0 );

Как и при создании пользовательского типа мы сформировали массив label, и указали что для создаваемой таксономии актуальна иерархическую структуру (т.е. могут быть родительский и дочерний элементы) — это свойственно рубрикам в обычных записях. В противном случае если структура не иерархическая — созданы обычные теги. Более подробно о таксономии Вы можете почитать тут.


Пользовательская таксономия (Категории продуктов)

Дополнительные области данных

Дополнительные области или блоки для ввода данных (meta boxes) вы могли видеть на странице редактирования записи. Все знают стандартные meta boxes, такие как выбор рубрики или тегов. Также в некоторых темах встречаются meta boxes позволяющие прикрепить картинку к записи и тд.

Так как мы создаем пользовательский тип «Продукция», то нам явно понадобится цена продукта, давайте рассмотрим процесс создания пользовательских meta boxes.

Процесс создания можно разделить на 3 этапа:

  • Определение самого meta boxes блока;
  • Определение содержимого (какие поля присутствуют в блоке);
  • Описание алгоритмов обработки введенных данных.

Определение meta boxes

1
2
3
4
5
6
7
8
9
10
11
add_action( 'add_meta_boxes', 'product_price_box' );
function product_price_box() {
    add_meta_box(
        'product_price_box',
        __( 'Цена продукта', 'myplugin_textdomain' ),
        'product_price_box_content',
        'product',
        'side',
        'high'
    );
}

Приведенный выше код создает блок со следующими параметрами:

  • product_price_box — уникальный идентификатор для meta box (он не обязательно должен совпадать с названием функции);
  • Цена продукта — название meta box, которое видит админ на странице;
  • product_price_box_content — функция, которая будет отображать содержимое окна;
  • product — название пользовательского типа записи, к которому принадлежит meta boxes;
  • side — положение блока на странице (side, normal или advanced — по-умолчанию);
  • high — приоритет meta boxes (в данном случае «высокий», блок находится в самом верху сайдбара. Варианты: high, core, low или default — по-умолчанию).

Определение содержимого

1
2
3
4
5
function product_price_box_content( $post ) {
   wp_nonce_field( plugin_basename( __FILE__ ), 'product_price_box_content_nonce' );
   echo '<label for="product_price"></label>';
   echo '<input type="text" id="product_price" name="product_price" placeholder="введите цену">';
}

Добавляем всего лишь одно поле, для ввода цены продукта. Заметьте название функции совпадает со значением третьего параметра при объявлении meta boxes (код выше).

Обработка введенных данных

Последний шаг — это сохранение введенной цены на продукт в базу данных.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
add_action( 'save_post', 'product_price_box_save' );
function product_price_box_save( $post_id ) {

   if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
   return;

   if ( !wp_verify_nonce( $_POST['product_price_box_content_nonce'], plugin_basename( __FILE__ ) ) )
   return;

   if ( 'page' == $_POST['post_type'] ) {
      if ( !current_user_can( 'edit_page', $post_id ) )
      return;
   } else {
      if ( !current_user_can( 'edit_post', $post_id ) )
      return;
   }
   $product_price = $_POST['product_price'];
   update_post_meta( $post_id, 'product_price', $product_price );
}

Большая часть функции это проверки. Прежде всего проверяется автосохранение, цена не будет сохранена до тех пор пока не будет нажата кнопка сохранить или обновить. После проверяются права доступа на редактирование данных этой записи и только после этого с помощью функции update_post_meta цена заносится в базу.


Meta Boxes

Отображение записей созданного типа на блоге

Все этапы создания пользовательского типа записей мы прошли, осталось воспользоваться плодами наших трудов, и сформировать отображение записей на блоге в соответствии с установленной нами таксономией.

Так как в процессе создании пользовательского типа мы указали истину для параметра has_archive то список записей типа product доступны по адресу http://mysite.com/product/.

Для отображения используется файл archive-[post_type].php (в нашем случае archive-product.php) если такой существует. Иначе, для отображения будет использован archive.php и если такой файл отсутствует в теме, то будет использовать index.php.

Еще один способ отображения записей пользовательского типа, это использование запроса класса WP_Query. В простой форме это выглядит примерно так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
   $args = array(
      'post_type' => 'product',
      'tax_query' => array(
         array(
            'taxonomy' => 'product_category',
            'field' => 'slug',
            'terms' => 'boardgames'
         )
      )
   );
   $products = new WP_Query( $args );
   if( $products->have_posts() ) {
      while( $products->have_posts() ) {
         $products->the_post();
         ?>
            <h1><?php the_title() ?></h1>
            <div class='content'>
               <?php the_content() ?>
            </div>
         <?php
      }
   }
   else {
      echo 'О нет, продукты не обнаружены!';
   }
?>

Отображение цены

Введенные дополнительные данные, в нашем случае цена продукта, могут быть получены с помощью функции get_post_meta (). Так как мы используем дополнительно поле product_price, то чтобы получить значение цены:

1
2
3
4
5
6
7
<?php
   // Если мы находимся в цикле, то легко можем получить id записи
   echo $price = get_post_meta( $post->ID, 'product_price', true );

   // Чтобы получить цены продуктов мы должны знать их id
   echo $price = get_post_meta( $product_id, 'product_price', true );
?>

Плагин для создания пользовательских типов записей

Если Вы не уверены в своих силах в области программирования, то всегда можно найти уже готовое решение (плагин) и воспользоваться им. Пользовательские типы не исключение. Плагин WCK Custom Post Type Creator позволяет вам легко создавать пользовательские типы записей для WordPress без знания программирования. СКАЧАТЬ.

Чтобы оставаться в курсе свежих статей и уроков подписывайтесь на еженедельную почтовую рассылку или на новостную ленту RSS. Спасибо!


Lingualeo

  • Подскажите, а как при custom post types выводить вложенность категорий?

    например завели пост тайп company, сделали категории Магазины, Услуги и т.д.
    создаем пост с компанией, указыаем ей категорию, но ссылки формируются так domain.com/company/postname а не domain.com/company/category_name/postname

    • Здравствуйте! надо настроить permalink для созданного пост тайпа, стандартные настройки не помогут.

      попробуйте так:

      function wptuts_custom_tags() {

      add_rewrite_rule(«^product/([^/]+)/([^/]+)/?»,’index.php?post_type=product&product_category=$matches[1]&product=$matches[2]’,’top’);

      }

      add_action(‘init’,’wptuts_custom_tags’);

      function wptuts_book_link( $post_link, $id = 0 ) {

      $post = get_post($id);

      if ( is_wp_error($post) || ‘product’ != $post->post_type || empty($post->post_name) )

      return $post_link;

      // Здесь получаем категорию:

      $terms = get_the_terms($post->ID, ‘product_category’);

      if( is_wp_error($terms) || !$terms ) {

      $genre = ‘uncategorised’;

      }

      else {

      $genre_obj = array_pop($terms);

      $genre = $genre_obj->slug;

      }

      return home_url(user_trailingslashit( «product/$genre/$post->post_name» ));

      }

      add_filter( ‘post_type_link’, ‘wptuts_book_link’ , 10, 2 );

      Чтобы наш пермалинк сработал надо зайти в настройки постоянных ссылок, и нажать кнопку «сохранить изменения», не важно какие там настройки.

    • мне как я понимаю надо product на свой пост тайп изменить? 🙂

    • совершенно верно и еще название таксономии (‘product_category’)

  • venz

    Дмитрий, прежде всего огромное спасибо за локализацию тута. Наиболее вменяемо преподнесено. Скажите, после сохранения значения из полей исчезают, это нормально? Вроде как обратная связь должна быть?

    • Рад стараться) В данном случае да, потому как значение не выставляется в мета бокс. Избавится от этого можно так, при определении содержимого получить значение(я)

      $value = get_post_meta( $post->ID, ‘product_price’, true );

      и вставить в инпут поле(я)

      value=»‘.$value.'»

    • venz

      Дмитрий, спасибо, но у меня данная конструкция не работает. Насколько я понял надо копать в сторону callback. По туту, дабы было все «в пачке» хорошо бы указать варианты положения блока на странице: side, normal … high…

    • venz

      Дмитрий, ваш вариант сработал, спасибо!

    • Пожалуйста! Варианты указал.

  • Алексей

    Здравствуйте, у меня есть проблема с произвольным типом записей… Ради теста полностью скопировал текст вашего кода, однако отображение все равно не работает… Таксономии создаются, пункты в меню тоже, однако все равно когда я «перехожу» к записи, то высвечивается белый экран и ничего больше… в адресной строке показывается, что я в нужном месте, но ничего не отображается. Тоже самое и с рубриками (как у вас написано должен браться archive.php, однако даже он не используется, хотя создал и специальный файл под произвольную запись) у меня высвечивается белый экран и ничего больше… из-за чего бы это могло быть? К слову говоря, используя плагин Magic Fields 2 таже проблема, значит проблема не в коде… Очень прошу помощи!

    • Алексей

      Версия ВП к слову 3.4.2 то есть должно работать

    • Здравствуйте, пробовали активировать другую тему, и вставить код в function.php активной темы? может ошибка в function.php… так как это wp, то может какой либо плагин давать такой конфликт либо если лазили в системные файлы wp что-нибудь повредили, мб стоит обновиться, если возможно.

    • Алексей

      Чтобы получить записи из определенной таксономии нужно не archive-[post_type].php, а taxonomy.php… ну и соответственно по иерархии: taxonomy-[name_taxonomy].php