Расширение WordPress с помощью произвольных типов контента

WordPress по умолчанию имеет много всего интересного. Он позволяет управлять контентом, как и многие другие решения с открытым кодом – и зачастую делает это даже лучше, чем многие коммерческие продукты. Один из самых привлекательных аспектов WordPress – это простота использования. Система проста, поскольку она лишена чрезмерных деталей, которые только усложняют процесс обучения.

Вследствие этого некоторые могут посчитать WordPress… слишком поверхностным и облегченным. Он позволяет сделать многое, но не все. Если вы пытались «перекроить» WordPress на свой лад, чтобы сделать какие-либо вещи, то, возможно, эта статья предназначается именно вам.

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

Если вы хотите сразу же ознакомиться с исходным кодом, мы подготовили его для вас (TXT-файл, 5 Кб).

Произвольные типы записей

WordPress позволяет вам легко и быстро расширить два стандартных типа данных (записи и страницы) до бесконечного массива, соответствующего вашим потребностям. Цифровым агентствам или фрилансерам пригодится тип записей Project (Проект). Торговому центру пригодится тип записей Location (Расположение).

Важная деталь. Отделение произвольных типов записей является хорошим подходом для контента, который должен кардинальным образом отличаться от записей или страниц. Возможно, вам нужно будет сделать так, чтобы пресс-релизы имели свой собственный тип записей. Однако зачастую пресс-релизы имеют вид записей и вносятся в рубрику «пресс-релизы». Возможно, вам нужно создать тип записей для посадочных страниц. Они могут быть представлены в виде отдельного типа записей, но зачастую представляются в виде страниц (Pages).

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

Регистрация типа записей

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

function create_post_type() {
  $labels = array(
    'name'               => 'Projects',
    'singular_name'      => 'Project',
    'menu_name'          => 'Projects',
    'name_admin_bar'     => 'Project',
    'add_new'            => 'Add New',
    'add_new_item'       => 'Add New Project',
    'new_item'           => 'New Project',
    'edit_item'          => 'Edit Project',
    'view_item'          => 'View Project',
    'all_items'          => 'All Projects',
    'search_items'       => 'Search Projects',
    'parent_item_colon'  => 'Parent Project',
    'not_found'          => 'No Projects Found',
    'not_found_in_trash' => 'No Projects Found in Trash'
  );

  $args = array(
    'labels'              => $labels,
    'public'              => true,
    'exclude_from_search' => false,
    'publicly_queryable'  => true,
    'show_ui'             => true,
    'show_in_nav_menus'   => true,
    'show_in_menu'        => true,
    'show_in_admin_bar'   => true,
    'menu_position'       => 5,
    'menu_icon'           => 'dashicons-admin-appearance',
    'capability_type'     => 'post',
    'hierarchical'        => false,
    'supports'            => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' ),
    'has_archive'         => true,
    'rewrite'             => array( 'slug' => 'projects' ),
    'query_var'           => true
  );

  register_post_type( 'sm_project', $args );
}

Ничего таинственного здесь нет. Мы вызываем функцию create_post_type, которая регистрирует наш тип записей. Мы передаем этому типу некоторые метки для бэкэнд-идентификации, а также список спецификаций, что именно этот тип может делать. Чтобы получить описание всех этих переменных, вы можете взглянуть в кодекс WordPress, однако ниже мы рассмотрим некоторые ключевые пункты.

Labels

Мы создали массив меток и просто передали его в массив аргументов. WordPress позволяет нам идентифицировать массу меток для самых разных целей.

Public

Данный параметр – родитель для нескольких других параметров, представленных в списке.  Значение по умолчанию для атрибута public – это false. Значение public передается следующим атрибутам, если они не определены явно: exclude_from_search, publicly_queryable, show_in_nav_menus и show_ui.

Exclude_from_search

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

Publicly_queryable

Этот атрибут используется только в фронтэнд-запросах и не затрагивает бэкэнд. Значение по умолчанию такое же, как у атрибута public. Обратите внимание, что если этот атрибут задан в false, то в таком случае вы не сможете просматривать тип записей во фронтэнде. К примеру, если вы хотите создать тип записей, который выводит на странице персонала список с именами и биографиями, но не хотите, чтобы у него был свой собственный URL на сайте, то в таком случае вам достаточно установить publicly_queryable в false.

Show_UI

В большинстве случаев show_ui устанавливается в true. Значение по умолчанию берется из атрибута public, однако может перезаписываться. Если задать атрибут в false, то в таком случае UI-элемент на странице консоли WordPress не будет вам доступен. Практическая причина, по которой вы можете задать его в false – ваш тип записей просто передает данные. К примеру, у вас может быть тип записей Events, который имеет повторяющийся атрибут. Когда вы сохраняете событие, создаются новые записи другого типа для обработки момента возникновения события. Вы хотите, чтобы UI выводил только базовый тип записей Events без метаданных для возникновения события.

Show_in_nav_menus

Достаточно простой и понятный атрибут. Если вы не хотите, чтобы этот тип записей был выведен в стандартном меню WordPress, задайте атрибут в false. Он берет значение public по умолчанию.

Show_in_menu

Вы можете изменить позицию типа записей в бэкэнде. При установке в true тип записей выводится как меню верхнего уровня (в той же иерархии, как записи и страницы). Если false, он не будет выведен вообще. Вы можете использовать строковое значение, чтобы вложить тип записей в подменю верхнего уровня. В строке можно задать «tools.php», что приведет к тому, что ваш тип записей будет вложенным элементом в меню Tools. Он берет стандартное значение от show_ui. Обратите внимание, что show_ui должен быть задан в true, чтобы вы могли управлять этим атрибутом.

Show_in_admin_bar

Ясно из названия. Если вы хотите, чтобы UI-элементы были добавлены в админ-бар, задайте этот атрибут в true.

Menu_position

Значение по умолчанию null поместит меню (верхнего уровня, если это не было изменено в show_in_menu) под разделом Comments в бэкэнде. Вы можете управлять этим атрибутом, указав целочисленное значение, которое будет указывать на позицию в стандартном меню WordPress. Значение 5 поместит тип записей сразу под Posts, 10 – под Media, 15 – под Links, 20 – под Pages. Полный список значений вы можете узнать в кодексе WordPress.

Menu_icon

Вы можете передать URL в данный атрибут, однако вы можете также просто использовать название иконки из Dashicons для быстрого решения. Передача названия dashicons-admin-appearance приведет к появлению кисти. Полный список Dashicons доступен здесь. Стандартной иконкой является чертежная кнопка, используемая для записей.

Capability_type

Этот атрибут используется в продвинутых концепциях сегментирования пользовательских ролей. Присвоение данному атрибуту значения post генерирует структуру возможностей (capability), которая работает в точности так же, как доступ к Posts. Используя это значение, подписчики не смогут получить доступ к данному типу записей, в то время как авторы, редакторы и администраторы смогут это сделать. Использование page в качестве значения приведет к тому, что доступ будет открыт только редакторам и администраторам. Вы можете определять более сложную структуру, используя атрибуты capability_type и capabilities в списке аргументов. Обратите внимание, что мы не используем атрибут capabilities в данном примере. Это продвинутая концепция, и она заслуживает отдельной статьи.

Hierarchical

Будет ли тип записей иерархическим (к примеру, страницей). Позволяет задавать родительский элемент. Если задано false, то тип записей ведет себя как Post.

Supports

К каждому новому типу записей можно подцепить целый набор стандартной функциональности. Этот массив сообщает WordPress, какой функционал подключить по умолчанию. Могут быть ситуации, когда вам не требуется редактор в вашем типе записей. Удаление этого пункта из массива приведет к тому, что поле редактора на странице редактирования записи исчезнет. В массив можно включать следующие элементы:

  • title
  • editor
  • author
  • thumbnail
  • excerpt
  • trackbacks
  • custom-fields
  • comments
  • revisions
  • page-attributes
  • post-formats

Has_archive

Если установлен в true, то в таком случае WordPress создаст иерархическую структуру для типа записей. Таким образом, вызов /projects/ приведет к появлению стандартного представления данных archive.php. Вы можете сформировать отдельный шаблон для данного архива, создав новый файл под названием archive-sm_project.php. Это поможет вам более тщательно управлять вашими архивами.

Rewrite

Опция rewrite позволяет вам формировать структуру URL для вашего типа записей. В нашем случае URL будет выглядеть следующим образом: http://www.example.com/projects/{slug}/, где slug – это строка, привязываемая к каждой записи при ее создании (обычно основана на заголовке записи). Вторая переменная может быть присвоена в массиве rewrite. Если вы добавите with_front => false (по умолчанию true), то в таком случае начальная часть URL, заданная в постоянных ссылках, не будет использоваться. К примеру, если структура постоянных ссылок у вас такова: /blog/%postname%/, следовательно, ваш произвольный тип записей будет автоматически следующим: /blog/projects/%postname%/. Это не очень-то хорошо, потому зададим with_front в false.

Query_var

Этот атрибут определяет, где именно вы можете использовать переменную запроса PHP для получения типа записей. По умолчанию true – обрабатывается со структурой постоянных ссылок. Вы можете использовать строку вместо переменной и управлять ключом переменной запроса со строковым значением.

Расширение типа записей таксономией (или двумя)

Записи в WordPress имеют связанные с ними рубрики и метки, которые позволяют вам правильно структурировать контент. Новые типы записей по умолчанию не имеют каких-либо таксономий, привязанных к ним. Возможно, вам попросту не нужны рубрики и метки в вашем типе записей, однако если все же нужны, вам потребуется зарегистрировать их. Имеете два вида таксономий, один из которых ведет себя как рубрики, а второй – как метки (без иерархической структуры). Их поведение в бэкэнде очень похоже (единственное различие – у рубрик могут быть дочерние элементы), однако то, как они представлены на экране администратора, может значительно различаться. Мы зарегистрируем две таксономии с разными типами.

function create_taxonomies() {

  // Add a taxonomy like categories
  $labels = array(
    'name'              => 'Types',
    'singular_name'     => 'Type',
    'search_items'      => 'Search Types',
    'all_items'         => 'All Types',
    'parent_item'       => 'Parent Type',
    'parent_item_colon' => 'Parent Type:',
    'edit_item'         => 'Edit Type',
    'update_item'       => 'Update Type',
    'add_new_item'      => 'Add New Type',
    'new_item_name'     => 'New Type Name',
    'menu_name'         => 'Types',
  );

  $args = array(
    'hierarchical'      => true,
    'labels'            => $labels,
    'show_ui'           => true,
    'show_admin_column' => true,
    'query_var'         => true,
    'rewrite'           => array( 'slug' => 'type' ),
  );

  register_taxonomy('sm_project_type',array('sm_project'),$args);

  // Add a taxonomy like tags
  $labels = array(
    'name'                       => 'Attributes',
    'singular_name'              => 'Attribute',
    'search_items'               => 'Attributes',
    'popular_items'              => 'Popular Attributes',
    'all_items'                  => 'All Attributes',
    'parent_item'                => null,
    'parent_item_colon'          => null,
    'edit_item'                  => 'Edit Attribute',
    'update_item'                => 'Update Attribute',
    'add_new_item'               => 'Add New Attribute',
    'new_item_name'              => 'New Attribute Name',
    'separate_items_with_commas' => 'Separate Attributes with commas',
    'add_or_remove_items'        => 'Add or remove Attributes',
    'choose_from_most_used'      => 'Choose from most used Attributes',
    'not_found'                  => 'No Attributes found',
    'menu_name'                  => 'Attributes',
  );

  $args = array(
    'hierarchical'          => false,
    'labels'                => $labels,
    'show_ui'               => true,
    'show_admin_column'     => true,
    'update_count_callback' => '_update_post_term_count',
    'query_var'             => true,
    'rewrite'               => array( 'slug' => 'attribute' ),
  );

  register_taxonomy('sm_project_attribute','sm_project',$args);
}

menu-preview

Отлично, теперь у нас есть две новых таксономии, привязанных к новому типу записей. Функция register_taxonomy получает три аргумента. Первый из них – это название таксономии, второй – это массив или строка с типами записей, третий – аргументы, определенные выше.

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

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

Добавление метаданных

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

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

acf-ui-preview

Используя ACF, вы можете задавать новые поля и на основании условий прикреплять их к контенту сайта. К примеру, мы можем создать мета-поле, которое будет показывать, сколько времени было потрачено на определенный проект. Мы можем добавить дополнительные поля для премий или создать поля для вывода списка ссылок на любой проект.

Использование ACF открывает пространство для всего того, что возможно в WordPress.

Добавление столбцов к экрану администратора

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

Before-preview

Давайте взглянем в код:

function columns($columns) {
  unset($columns['date']);
  unset($columns['taxonomy-sm_project_attribute']);
  unset($columns['comments']);
  unset($columns['author']);
  return array_merge(
    $columns,
    array(
      'sm_awards' => 'Awards',
      'sm_timeframe' => 'Timeframe'
    ));
}

Первая строка сбрасывает столбец даты. Вы можете сбросить любой дефолтный столбец. Вторая строка сбрасывает произвольную таксономию, которую мы зарегистрировали (напоминающую метки). Это может оказаться полезным для поддержания экрана администратора чистым и удобным. Как вы могли видеть, мы также сбросили комментарии и автора – информацию, которая не так важна на этом экране.

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

function column_data($column,$post_id) {
  switch($column) {
  case 'sm_awards' :
    echo get_post_meta($post_id,'awards',1);
    break;
  case 'sm_timeframe' :
    echo get_post_meta($post_id,'timeframe',1);
    break;
}

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

After-preview

Сортировка

Переходим к сортировке. Как вы уже знаете, WordPress сортирует страницы на основе порядка меню, а затем в алфавитном порядке по заголовку; записи сортируются по дате публикации. Давайте отсортируем наш новый тип записей по количеству премий. Мы хотим сделать так, чтобы работы, которые получили больше всего премий, выводились всегда в самом верху списка. Если мы используем стандартные запросы WordPress, порядок, который мы установим, будет выполняться на всем сайте. Нам понадобится функция для подключения к таблицам wp_posts и wp_postmeta, а также некоторым другим, чтобы пересмотреть сортировку данных.

function join($wp_join) {
  global $wpdb;
  if(get_query_var('post_type') == 'sm_project') {
    $wp_join .= " LEFT JOIN (
      SELECT post_id, meta_value as awards
      FROM $wpdb->postmeta
      WHERE meta_key =  'awards' ) AS meta
      ON $wpdb->posts.ID = meta.post_id ";
  }
  return ($wp_join);
}

Функция делает подключение за нас. Мы не будем рассматривать то, как работает этот оператор select (это тема для отдельной статьи). Обратите внимание на оператор if. Мы определяем тип записей и выполняем join, если он отвечает условиям sm_project. Если опустить это условие if, подключение join устанавливалось бы вне зависимости от типа, что нам не требуется.

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

function set_default_sort($orderby,&$query) {
  global $wpdb;
  if(get_query_var('post_type') == 'sm_project') {
    return "meta.awards DESC";
  }
  return $orderby;
}

Мы проверяем наш тип записей и затем возвращаем исправленный оператор order. Далее мы говорим WordPress, что нужно сортировать по соответствующему значению из таблицы wp_postmeta в убывающем порядке. Таким образом, мы получаем список наших премий, начиная с самого эффективного проекта (больше всего премий) и заканчивая менее удачным.

Объединяем все это воедино

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

class sm_project {

  function __construct() {
    add_action('init',array($this,'create_post_type'));
    add_action('init',array($this,'create_taxonomies'));
    add_action('manage_sm_project_posts_columns',array($this,'columns'),10,2);
    add_action('manage_sm_project_posts_custom_column',array($this,'column_data'),11,2);
    add_filter('posts_join',array($this,'join'),10,1);
    add_filter('posts_orderby',array($this,'set_default_sort'),20,2);
  }

  function create_post_type() {
    …
  }

  function create_taxonomies() {
    …
  }

  function columns($columns) {
    …
  }

  function column_data($column,$post_id) {
    …
  }

  function join($wp_join) {
    …
  }

  function set_default_sort($orderby,&$query) {
    …
  }
}

new sm_project();

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

Источник: http://www.smashingmagazine.com 

Блог про WordPress
Комментарии: 11
  1. Василий

    Здравствуйте.

    Подскажите. У меня ест произвольный тип данных «Игры». Я там создаю запись с доп полями. Как сделать меню с выводом записей (новости, видео, фотографии) относящиеся к этой игре. Пример тут http://gauge.wpengine.com/call-of-duty-ghosts/ Т.е. чтобы была родительная страница игры и дочернии страницы с различной информацией и выводом записей.

    Кто мб делал так? Направьте, где можно почитать, как такое можно сделать.

  2. zxxz

    Думаю, вам нужно для вашего типа «Игры» сделать неирерхическую таксономию, типа теги. В качестве имени тега будет выступать имя игры. Шаблоном страницы игры будет файл tag-{slug}.php в котором циклом выводить посты тегированные названием игры. Условие вывода задавать через WP_Query с указанием таксономии и имени тега. Наверное, как-то так. Я сам только начинаю разбираться с wordpress, могу и ошибаться.

  3. Dima

    Очередной респект и уважуха за перевод!!!!
    СПАСИБО!

    1. Дмитрий (автор)

      Спасибо!

  4. Василий

    А как добиться вида чпу Пример тут /call-of-duty-ghosts/news/ или другой любой приставки к названию игры? Ведь если делать таксономию, как метки, то там будет такого вида /tag/news/. «Tag» конечно можно заменить на своё, но как прикрутить такую страницу к каждой странице с игрой.

    Поиски продолжаются :)

    1. zxxz

      Думаю, только через Rewrite API, но сам бы хотел услышать правильный ответ.

      1. Дмитрий (автор)

        Можете почитать вот тут (на англ.):

        http://thereforei.am/2011/10/28/advanced-taxonomy-queries-with-pretty-urls/

  5. Дмитрий

    Здравствуйте,у меня возник вопрос .Как мне настроить кол-во выводимых записей на странице к примеру в моем новом собственном типе.То есть в WP есть настройка максимального выводимого числа записей в arhive.php. Но для нового типа записей к примеру Игры(single-games.php) — я хочу что б в архиве (arhive-games.php) — выводилось иное кол-во нежели в общих настройках WP. То есть в играх 7 последних на странице в Домах по 3 последних записи на странице и тд.
    Заранее спасибо.

  6. Игорь

    Добрый день! У меня возникла такая проблема. У меня стоит Woocommerce.
    Есть много категорий, но теперь возникла потребность выводить эти категории с другим стилем и дополнительными фишками.

    К примеру: У меня есть категория женская одежда на странице артикула которой выводится таблица женских размеров. А на странице мужской одежды таблица мужской одежды.
    А на странице артикулов бейджей должна быть внизу форма заказа нового бейджа с возможностью создать свой макет.

    Вопрос: Можно мне создать произвольный тип записи на основе шаблона товара Woocommerce и как его создать?(обыскал все, но не смог найти решение проблемы).
    Или может как-то можно создать макет категории товара? Спасибо за внимание.

  7. Максим

    Здравствуйте.
    Вопрос касается привязки существующей таксономии «метки» к новому произвольному типу данных.
    Сама функция понятна. Вот она: «register_taxonomy_for_object_type».
    И в админке всё работает.
    Но вот во фронтенде почему-то не показываются архивы постов произвольного типа по соответствующей метке. Обычные посты показываются, а произвольного типа — нет.
    Может быть подскажете, в каком направлении искать?
    Спасибо.

  8. Сергей

    Здравствуйте!
    Прошу Вас уточнить как использовать Exclude_from_search
    У меня на сайте установлен плагин basic google maps placemarks и прописаны метки на карте. При поиске по сайту выводится и статья и метка. Мне нужно что-бы в результатах поиска выводилась только статья.Куда именно нужно внедрить Exclude_from_search чтобы это заработало?

Добавить комментарий для Дмитрий Отменить ответ

Получать новые комментарии по электронной почте.