WordPress. Виджет «Дерево категорий»

10.07.2019

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

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

При клике по категории, у которой есть дочерние категории — будут показаны дочерние. Если у категории нет потомков — тогда будет просто переход на страницу категории. Практической ценности этот плагин не имеет, сделан исключительно с целью изучения WordPress.

Простой вариант

Итак, создаем директорию category-tree-widget, а внутри нее — три файла: category-tree-widget.php, category-tree-widget.js и Category_Tree_Widget.php.

<?php
/*
Plugin Name: Виджет «Дерево категорий»
Plugin URI: https://tokmakov.msk.ru
Description: Добавляет виджет категорий блога в виде дерева
Version: 1.0
Author: Евгений Токмаков
Author URI: https://tokmakov.msk.ru
*/

require 'Category_Tree_Widget.php';

/*
 * Регистрируем виджет «Дерево категорий»
 */
add_action(
    'widgets_init',
    function () {
        register_widget('Category_Tree_Widget');
    }
);

/*
 * Подключаем js-файл, который будет изначально скрывать все дочерние
 * категории и показывать их по клику на родительской категории
 */
add_action(
    'wp_enqueue_scripts',
    function () {
        wp_enqueue_script(
            'category-tree-widget', // будет зарегистрирован под этим именем
            plugin_dir_url(__FILE__) . 'category-tree-widget.js',
            [ // должен быть подключен после jquery
                'jquery',
            ],
            null, // версии нет, поэтому null
            true // подключаем перед закрывающим тегом body
        );
    }
);
<?php
/**
 * Класс виджета, который позволяет вывести категории
 * блога в виде дерева
 */
class Category_Tree_Widget extends WP_Widget {

    /**
     * Cоздание виджета
     */
    function __construct() {
        parent::__construct(
            'category_tree_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'];
        }

        // выводим дерево категорий
        echo '<ul id="category-tree-widget">', "\n";
        wp_list_categories(
            apply_filters(
                'widget_categories_args',
                ['title_li' => ''], 
                $instance
            )
        );
        echo '</ul>', "\n";

        echo $args['after_widget'];

    }

    /*
     * Форма настроек виджета в панели управления
     */
    public function form($instance) {

        $title = '';
        if (isset($instance['title'])) {
            $title = $instance['title'];
        }

        // заголовок виджета
        $id = $this->get_field_id('title');
        $name = $this->get_field_name('title');
        $value = esc_attr($title);
        ?>
        <p>
        <label for="<?= $id; ?>">Заголовок</label>
        <input type="text" class="widefat" id="<?= $id; ?>"
               name="<?= $name; ?>" value="<?= $value; ?>" />
        </p>
        <?php

    }

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

}
jQuery(document).ready(function ($) {
    $('#category-tree-widget ul').hide();
    $('#category-tree-widget li a').on('click', function (event) {
        /*
         * Если у этого элемента дерева есть дочерние узлы и они в данный момент
         * свернуты, то по ссылке переходить не надо, а надо их просто показать.
         */
        var closed = $(this).next().is('ul') && !$(this).next().is(':visible')
        if (closed) {
            event.preventDefault();
            $(this).next().slideDown('normal');
        }
    });
});

После установки и активации плагина, виджет станет доступным в числе прочих и его можно будет разместить в сайдбаре. Этот виджет сформирует примерно такой html-код:

<ul id="category-tree-widget">
    <li class="cat-item cat-item-123"><a href="...">Первая категория</a></li>
    <li class="cat-item cat-item-197 current-cat-parent current-cat-ancestor"><a href="...">Вторая категория</a>
        <ul class="children">
            <li class="cat-item cat-item-206 current-cat"><a href="...">Дочерняя категория</a></li>
        </ul>
    </li>
    <li class="cat-item cat-item-196"><a href="...">Третья категория</a>
        <ul class="children">
            <li class="cat-item cat-item-204"><a href="...">Дочерняя категория</a>
                <ul class="children">
                    <li class="cat-item cat-item-205"><a href="...">Дочерняя дочерней</a></li>
                </ul>
            </li>
        </ul>
    </li>
    <li class="cat-item cat-item-198"><a href="...">Четвертая категория</a></li>
</ul>

Сложный вариант

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

<?php
/*
Plugin Name: Виджет «Дерево категорий»
Plugin URI: https://tokmakov.msk.ru
Description: Добавляет виджет категорий блога в виде дерева
Version: 1.0
Author: Евгений Токмаков
Author URI: https://tokmakov.msk.ru
*/

require 'Category_Tree_Widget.php';

/*
 * Регистрируем виджет «Дерево категорий»
 */
add_action(
    'widgets_init',
    function () {
        register_widget('Category_Tree_Widget');
    }
);

/*
 * Подключаем css и js файлы виджета «Дерево категорий»
 */
add_action(
    'wp_enqueue_scripts',
    function () {
        /*
         * Подключаем css-файл
         */
        wp_enqueue_style(
            'category-tree-widget', // будет зарегистрирован под этим именем
            plugin_dir_url(__FILE__) . 'category-tree-widget.css',
            [ // должен быть подключен после bootstrap
                'current-theme-bootstrap',
            ]
        );
        /*
         * Подключаем js-файл
         */
        wp_enqueue_script(
            'category-tree-widget', // будет зарегистрирован под этим именем
            plugin_dir_url(__FILE__) . 'category-tree-widget.js',
            [ // должен быть подключен после jquery
                'jquery',
            ],
            null, // версии нет, поэтому null
            true // подключаем перед закрывающим тегом body
        );
    }
);
<?php
/**
 * Класс виджета, который позволяет вывести категории
 * блога в виде дерева
 */
class Category_Tree_Widget extends WP_Widget {

    /**
     * Cоздание виджета
     */
    function __construct() {
        parent::__construct(
            'category_tree_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'];

    }

    /*
     * Форма настроек виджета в панели управления
     */
    public function form($instance) {

        $title = '';
        if (isset($instance['title'])) {
            $title = $instance['title'];
        }

        // заголовок виджета
        $id = $this->get_field_id('title');
        $name = $this->get_field_name('title');
        $value = esc_attr($title);
        ?>
        <p>
            <label for="<?= $id; ?>">Заголовок</label>
            <input type="text" class="widefat" id="<?= $id; ?>"
                   name="<?= $name; ?>" value="<?= $value; ?>" />
        </p>
        <?php

    }

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

    /*
     * Выводит список категорий блога в виде вложенного списка
     */
    private function tree($parent) {
        $categories = get_categories([
            'hide_empty' => true,
            'parent' => $parent
        ]);
        if (!empty($categories)) {
            $icons = [
                'plus' => '<b class="glyphicon glyphicon-plus" aria-hidden="true"></b>',
                'right' => '<i class="glyphicon glyphicon-triangle-right" aria-hidden="true"></i>',
            ];
            ?>
            <ul<?= $parent ? '' : ' id="category-tree-widget"'; ?>>
                <?php foreach ($categories as $category): ?>
                    <?php
                    $icon = $icons['right'];
                    if ($this->hasChildren($category->term_id)) {
                        $icon = $icons['plus'];
                    }
                    $link = get_category_link($category->term_id);
                    ?>
                    <li><?= $icon; ?> <a href="<?= $link; ?>"><?= $category->name; ?></a>
                        <?php $this->tree($category->term_id); ?>
                    </li>
                <?php endforeach; ?>
            </ul>
            <?php
        }
    }

    /*
     * Возвращает true, если у категории блога есть дочерние категории
     */
    private function hasChildren($id) {
        $categories = get_categories([
            'hide_empty' => true,
            'parent' => $id
        ]);
        return !empty($categories);
    }
}
jQuery(document).ready(function ($) {
    $('#category-tree-widget ul').hide();
    $('#category-tree-widget li b').on('click', function () {
        var $icon = $(this);
        var closed = $icon.siblings('ul') && !$icon.siblings('ul') .is(':visible');

        if (closed) {
            $icon.siblings('ul').slideDown('normal', function () {
                $icon.removeClass('glyphicon-plus').addClass('glyphicon-minus');
            });
        } else {
            $icon.siblings('ul').slideUp('normal', function () {
                $icon.removeClass('glyphicon-minus').addClass('glyphicon-plus');
            });
        }
    });
});
#category-tree-widget {
    list-style: none;
    padding-left: 0;
}
    #category-tree-widget ul {
        list-style: none;
        padding-left: 25px;
    }
    #category-tree-widget .glyphicon {
        color: #777;
        border: 1px solid #777;
        padding: 1px;
        border-radius: 2px;
        font-size: 10px;
        margin-right: 5px;
    }
    #category-tree-widget .glyphicon-plus,
    #category-tree-widget .glyphicon-minus {
        cursor: pointer;
        color: #337ab7;
        border: 1px solid #337ab7;
    }

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

Каталог оборудования
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.