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

06.09.2019

Теги: CMSWeb-разработкаWordPressВиджетИерархияНавигацияПлагинТаксономия

В WordPress по умолчанию уже есть несколько типов записей: посты (тип post), страницы (тип page), вложения (тип attachment), редакции (тип revision) и элементы меню (тип nav_menu_item). Посты предназначены для публикации статей блога и имеют две таксономии: рубрики и метки. Страницы в целом похожи на посты, но имеют несколько уникальных особенностей — например, поддерживают иерархию.

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

Давайте напишем плагин, который зарегистрирует новый тип записи book (книга) и добавит для него иерархическую таксономию genre (жанр). Еще напишем класс виджета для навигации по книгам и зарегистрируем его в коде плагина.

Плагин «Каталог книг»

Итак, создаем директорию tokmakov-book и внутри нее — файл tokmakov-book.php:

<?php
/*
Plugin Name: Каталог книг
Plugin URI: https://tokmakov.msk.ru
Description: Позволяет создать на сайте простой каталог книг с таксономией по жанрам.
Version: 1.0
Author: Евгений Токмаков
Author URI: https://tokmakov.msk.ru
*/

register_activation_hook(__FILE__, function() {
    // проверяем права пользователя на установку плагинов
    if (!current_user_can('activate_plugins')) {
        return;
    }
});

register_deactivation_hook(__FILE__, function() {
    // проверяем права пользователя на деактивацию плагинов
    if (!current_user_can('deactivate_plugins')) {
        return;
    }
});

Регистрируем пользовательский тип записи book:

/*
 * Регистрируем пользовательский тип записи book
 */
add_action('init', function () {
    $labels = [
        'name' => 'Книги',
        'menu_name' => 'Книги',
        'singular_name' => 'Книга',
        'add_new' => 'Добавить книгу',
        'add_new_item' => 'Добавить новую книгу',
        'edit_item' => 'Редактировать книгу',
        'new_item' => 'Новая книга',
        'all_items' => 'Все книги',
        'view_item' => 'Посмотреть книгу',
        'search_items' => 'Найти книги',
        'not_found' =>  'Ничего не найдено',
        'not_found_in_trash' => 'В корзине не найдено'
    ];
    $args = [
        'labels' => $labels,
        'public' => true,
        'publicly_queryable' => true,
        'show_ui' => true,
        'show_in_menu' => true,
        'query_var' => true,
        'rewrite' => true,
        'capability_type' => 'post',
        'has_archive' => true,
        'hierarchical' => false,
        'menu_position' => null,
        'supports' => [
            'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields'
        ],
        'taxonomies' => ['genre'],
    ];
    register_post_type('book', $args);
});

Регистрируем иерархическую таксономию — жанры книг:

/*
 * Регистрируем иерархическую таксономию по жанрам
 */
add_action('init', function () {
    $labels = array(
        'name'          => 'Жанры',
        'singular_name' => 'Жанр',
        'menu_name'     => 'Жанры' ,
        'all_items'     => 'Все жанры',
        'edit_item'     => 'Редактировать жанр',
        'view_item'     => 'Посмотреть жанр',
        'update_item'   => 'Сохранить жанр',
        'add_new_item'  => 'Добавить новый жанр',
        'parent_item'   => 'Родительский жанр',
        'search_items'  => 'Поиск по жанрам',
        'back_to_items' => 'Назад на страницу жанров',
        'most_used'     => 'Популярные жанры',
    );
    $args = array(
        'labels'            => $labels,
        'show_admin_column' => true,
        'hierarchical'      => true,
    );
    register_taxonomy('genre', ['book'], $args);
});

Создаем виджет для навигации

Нам потребуется файл класса виджета Book_Genres_Widget.php, в который мы добавим следующий код:

<?php
/**
 * Класс виджета, который позволяет вывести все жанры книг
 * в виде многоуровневого списка
 */
class Book_Genres_Widget extends WP_Widget {

    /**
     * Cоздание виджета
     */
    function __construct() {
        parent::__construct(
            'book_genres_widget',
            'Жанры книг', // заголовок виджета
            ['description' => 'Все жанры книг в виде дерева'] // описание
        );
    }

    /**
     * Метод выводит жанры книг в общедоступной части сайта
     */
    public function widget($args, $instance) {

        // к заголовку применяем фильтр
        $title = apply_filters('widget_title', $instance['title']);

        echo $args['before_widget'];

        // выводим заголовок виджета
        if ( ! empty($title)) {
            echo $args['before_title'] . $title . $args['after_title'];
        }

        // выводим жанры книг в иерархическом виде
        $this->tree(0);

        echo $args['after_widget'];
    }

    private function tree($parent) {
        $terms = get_terms([
            'taxonomy' => 'genre',
            'hide_empty' => false,
            'parent' => $parent
        ]);

        if (!empty($terms)) {
            ?>
            <ul<?= $parent ? '' : ' id="book-genres-widget"'; ?>>
                <?php foreach ($terms as $term): ?>
                    <?php $link = get_term_link($term->term_id, 'genre'); ?>
                    <li><a href="<?= $link; ?>"><?= $term->name; ?></a>
                        <?php $this->tree($term->term_id); ?>
                    </li>
                <?php endforeach; ?>
            </ul>
            <?php
        }
    }

    /*
     * Форма настроек виджета в панели управления
     */
    public function form($instance) {
        $title = '';
        if (isset($instance['title'])) {
            $title = $instance['title'];
        }
        ?>
        <p>
            <label for="<?= $this->get_field_id('title'); ?>">Заголовок</label>
            <input type="text" class="widefat"
                   id="<?= $this->get_field_id('title'); ?>"
                   name="<?= $this->get_field_name('title'); ?>'"
                   value="<?= esc_attr($title); ?>" />
        </p>
        <?php
    }

    /*
     * Сохранение настроек виджета в панели управления
     */
    public function update($new_instance, $old_instance) {
        $instance = array();
        $instance['title'] =
            ! empty($new_instance['title']) ? strip_tags($new_instance['title']) : '';
        return $instance;
    }
}

Теперь подключим файл класса в коде плагина и зарегистрируем виджет:

require 'Book_Genres_Widget.php';

/*
 * Регистрируем виджет «Жанры книг»
 */
add_action(
    'widgets_init',
    function () {
        register_widget('Book_Genres_Widget');
    }
);

Далее, переходим в панель управления, активируем плагин, добавляем несколько жанров и несколько книг:

Классика
    Русская классика
    Зарубежная классика
Детективы
    Исторические детективы
    Современные детективы
Фантастика
    Научная фантастика
    Космическая фантастика

Размещаем наш виджет в сайдбаре, для возможности навигации по каталогу книг:

Все, наш каталог книг и навигация для него уже работают:

Шаблоны для показа книг

В нашем случае запись типа book мало чем отличается от записи типа post. Так что для показа записей типа book вполне подходят шаблоны для записей типа post. Но в общем случае это может быть не так, и для книг могут потребоваться отдельные шаблоны. Так что давайте создадим шаблоны: single-book.php — для показа отдельной книги и taxonomy-genre.php — для показа списка книг выбанного жанра:

<?php
/**
 * Шаблон для показа отдельной записи типа book, файл single-book.php
 */

/*
 * Подключаем шапку сайта
 */
get_header();
?>

<div id="content">
    <div class="container">
        <div class="row">
            <main class="col-md-9">
                <?php get_template_part('parts/single-book'); ?>
            </main>
            <aside class="col-md-3">
                <?php get_sidebar(); ?>
            </aside>
        </div>
    </div>
</div>

<?php
/*
 * Подключаем подвал сайта
 */
get_footer();
?>
<?php
/**
 * Шаблон для показа отдельной записи типа book, файл parts/single-book.php
 */
?>

<?php if (have_posts()): ?>
    <div id="clean-single">
        <?php the_post(); ?>
        <h1><?php the_title(); ?></h1>
        <div class="row">
            <div class="col-md-5">
                <?= get_the_post_thumbnail(null, 'full'); ?>
            </div>
            <div class="col-md-7">
                <?php the_content(); ?>
            </div>
        </div>
        <div class="well well-sm" style="margin-top: 1em">
            Категории:
            <?=
            get_the_term_list(
                get_the_ID(),
                'genre',
                '',
                ', ',
                ''
            );
            ?>
        </div>
        <ul class="pager">
            <li class="previous">
                <?php
                previous_post_link(
                    '%link',
                    __('Предыдущая', 'clean')
                );
                ?>
            </li>
            <li class="next">
                <?php
                next_post_link(
                    '%link',
                    __('Следующая', 'clean')
                );
                ?>
            </li>
        </ul>
    </div>
<?php endif; ?>
<?php
/**
 * Шаблон для показа списка записей с таксономией genre, файл taxonomy-genre.php
 */

/*
 * Подключаем шапку сайта
 */
get_header();
?>

<div id="content">
    <div class="container">
        <div class="row">
            <main class="col-md-9">
                <h1><?php the_archive_title(); ?></h1>
                <p><?php the_archive_description(); ?></p>
                <?php get_template_part('parts/list-book'); ?>
            </main>
            <aside class="col-md-3">
                <?php get_sidebar(); ?>
            </aside>
        </div>
    </div>
</div>

<?php
/*
 * Подключаем подвал сайта
 */
get_footer();
?>
<?php
/**
 * Список записей типа book с постраничной навигацией, файл parts/list-book.php
 */
?>

<?php if (have_posts()): ?>
    <ul class="media-list">
        <?php while (have_posts()): ?>
            <?php the_post(); ?>
            <li class="media well">
                <div class="media-left">
                    <?php
                    $default = get_template_directory_uri().'/assets/img/default-background.jpg';
                    $thumbnail = get_the_post_thumbnail_url(null, 'thumbnail') ?: $default;
                    ?>
                    <img class="media-object" src="<?= $thumbnail; ?>" alt="">
                </div>
                <div class="media-body">
                    <h4 class="media-heading"><?php the_title(); ?></h4>
                    <p><?php the_excerpt(); ?></p>
                    <p>
                        <a href="<?php the_permalink(); ?>" class="btn btn-default">
                            <?php _e('Читать дальше', 'clean'); ?>
                        </a>
                    </p>
                </div>
            </li>
        <?php endwhile; ?>
    </ul>
    <?php the_posts_pagination(); ?>
<?php else: ?>
    <p><?php _e('Ничего не найдено', 'clean'); ?></p>
<?php endif; ?>

Добавляем каталог в меню

Записи типа post могут быть показаны на главной странице сайта, либо на статической странице сайта. Это задается в настройках в панели управления:

Вместе с записями типа post мы можем вывести и записи типа book. Для этого достаточно добавить в наш плагин следующий код:

/*
 * Показываем книги на главной странице вместе с постами блога
 */
add_action('pre_get_posts', function ($query) {
    if (is_home() && $query->is_main_query()) {
        $query->set('post_type', ['post', 'book']);
    }
    return $query;
});

Но мы можем показать на главной странице только книги, для этого нужно лишь немного изменить код:

/*
 * Показываем на главной странице только книги
 */
add_action('pre_get_posts', function ($query) {
    if (is_home() && $query->is_main_query()) {
        $query->set('post_type', ['book']);
    }
    return $query;
});

А теперь представим такую ситуацию. Есть сайт, на главной странице показывается статическая страница. Записи типа post показываются на статической странице «Блог», эта страница есть в главном меню. И мы хотим добавить в главное меню еще один пункт «Книги». Для этого создаем статическую страницу со слагом books. И создадим для этой страницы шаблон page-books.php в директории темы:

<?php
/**
 * Шаблон для показа списка записей типа book с
 * постраничной навигацией, файл page-books.php
 */

/*
 * Подключаем шапку сайта
 */
get_header();
?>

<div id="content">
    <div class="container">
        <div class="row">
            <main class="col-md-9">
                <?php the_post(); ?>
                <h1><?php the_title(); ?></h1>
                <?php the_content(); ?>
                <?php
                // изменяем основной запрос, теперь он будет содержать не данные
                // о статической странице books, а данные о записях типа book
                query_posts([
                    'post_type' => 'book',
                    'publish' => true,
                    'paged' => get_query_var('paged'),
                ]);
                get_template_part('parts/list-book');
                ?>
            </main>
            <aside class="col-md-3">
                <?php get_sidebar(); ?>
            </aside>
        </div>
    </div>
</div>

<?php
/*
 * Подключаем подвал сайта
 */
get_footer();
?>

Создаем статическую страницу:

Добавляем эту страницу в меню:

И вот что получилось в итоге:

Оказывается, сразу после регистрации нового типа записи book, в публичной части сайта доступна страница /book/. Если она все-таки недоступна — надо в панели управления перейти на страницу настроек постоянных ссылок и нажать кнопку «Сохранить изменения». В качестве шаблона эта страница использует файл archive.php темы, но лучше создать отдельный шаблон archive-book.php.
Для того, чтобы добавить в меню еще один пункт «Книги» — не нужно создавать статическую страницу со слагом books и шаблон page-books.php, как мы это делали выше. Достаточно просто добавить в меню произвольную ссылку http://server.com/book/. Иначе, мы получим две абсолютно одинаковые страницы со списком всех книг, но доступные по разным адресам /book/ и /books/.

Поиск: CMS • Web-разработка • WordPress • Виджет • Иерархия • Навигация • Плагин • Таксономия • register_taxonomy • register_post_type

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.