WordPress. Создание простой темы

18.05.2019

Теги: CMSWeb-разработкаWordPressШаблонСайта

Темы WordPress находятся в поддиректориях wp-content/themes. Директория темы содержит файл стилей style.css, около дюжины php-шаблонов и файл дополнительного функционала functions.php. Файлы index.php и style.css являются обязательными. Файл стилей style.css обязательно должен содержать информацию о теме в комментарии.

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

Создаем директорию темы clean и добавляем в нее файлы style.css и index.php:

/*
Theme Name: clean
Version: 1.0.0
Text Domain: clean
*/
<?php
/**
 * Файл index.php. Максимально универсальный шаблон, используется для показа
 * любых страниц сайта, если не был найден более подходящий шаблон
 */

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

<p><?php _e('Не найден шаблон для показа этой страницы', 'clean'); ?></p>

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

Создаем поддиректорию assets, копируем в нее все css и js файлы шаблона. Создаем файл functions.php, который формально не является обязательным, но без которого тему не создать:

<?php
add_action('after_setup_theme', 'current_theme_setup');

function current_theme_setup() {

    /*
     * Загружаем файл перевода темы в память
     */
    load_theme_textdomain('clean', get_template_directory() . '/languages');

    /*
     * Включаем поддержку форматов постов (это редко используется)
     */
    add_theme_support(
        'post-formats',
        [
            'audio', 'video', 'gallery',
            /*
            'aside', 'link', 'image',
            'quote', 'status', 'chat'
            */
        ]
    );

    /*
     * Добавляем поддержку автоматического добавления тега <title>
     */
    add_theme_support('title-tag');

    /*
     * Добавляем поддержку миниатюр для всех типов постов
     */
    add_theme_support('post-thumbnails');

    /*
     * Регистрируем расположение для двух меню — в шапке и в подвале
     */
    register_nav_menus([
        'header_menu' => esc_html__('Меню в шапке', 'clean'),
        'footer_menu' => esc_html__('Меню в подвале', 'clean')
    ]);

    /*
     * Добавляем возможность загрузки своего фона для <body>
     */
    add_theme_support(
        'custom-background',
        array(
            'default-color' => 'FFFFFF',
            'default-image' => '',
        )
    );

    /*
     * Добавляем возможность установки фона для шапки сайта. Изменять
     * можно не только фоновое изображение, но и цвет заголовка.
     */
    add_theme_support(
        'custom-header',
        [
            'default-color' => 'FFFFFF',
            'default-text-color' => '000000',
            'default-image' => '',
            'width' => 3000,
            'height' => 500,
        ]
    );

    /*
     * Добавляем возможность загрузки своего логотипа
     */
    add_theme_support(
        'custom-logo',
        array(
            'height' => 250,
            'width' => 250,
            'flex-width' => true,
            'flex-height' => true,
        )
    );

    /*
     * Добавляем поддержку selective refresh (выборочное обновление) в
     * Theme Customizer для виджетов
     */
    add_theme_support('customize-selective-refresh-widgets');
}

/*
 * Регистрируем место для сайдбара в боковой колонке
 */
add_action(
    'widgets_init',
    function () {
        register_sidebar(
            array(
                'name' => esc_html__('Боковая колонка', 'clean'),
                'id' => 'sidebar_right',
                'description' => esc_html__(
                    'Перетащите сюда виджеты, чтобы добавить их в сайдбар',
                    'clean'
                ),
                'before_widget' => '<section id="%1$s" class="widget %2$s">',
                'after_widget' => '</section>',
                'before_title' => '<h3 class="widget-title">',
                'after_title' => '</h3>',
            )
        );
    }
);

/*
 * Подключаем файлы стилей и скриптов
 */
add_action(
    'wp_enqueue_scripts',
    function () {

        /*
         * Подключаем файлы стилей
         */
        wp_enqueue_style(
            'current-theme-bootstrap', // будет зарегистрирован под этим именем
            get_template_directory_uri() . '/assets/bs/css/bootstrap.min.css'
        );
        wp_enqueue_style(
            'current-theme-style', // будет зарегистрирован под этим именем
            get_template_directory_uri() . '/assets/css/style.css',
            // должен быть подключен после этих css-файлов
            [
                'current-theme-bootstrap'
            ]
        );
        wp_enqueue_style(
            'default-theme-style', // будет зарегистрирован под этим именем
            get_stylesheet_uri(),
            // должен быть подключен после этих css-файлов
            [
                'current-theme-bootstrap',
                'current-theme-style'
            ]
        );

        /*
         * Подключаем файлы скриптов
         */
        wp_enqueue_script(
            'current-theme-bootstrap', // будет зарегистрирован под этим именем
            get_template_directory_uri() . '/assets/bs/js/bootstrap.min.js',
            array('jquery'), // должен быть подключен после jquery
            null, // версии нет, поэтому null
            true // подключаем перед закрывающим тегом body
        );
        wp_enqueue_script(
            'current-theme-script', // будет зарегистрирован под этим именем
            get_template_directory_uri() . '/assets/js/script.js',
            array('jquery'), // должен быть подключен после jquery
            null, // версии нет, поэтому null
            true // подключаем перед закрывающим тегом body
        );
    }
);

/*
 * Задаем css-стили для элементов меню навигации, чтобы использовать
 * готовые стили Bootstrap 3
 */
add_filter(
    'nav_menu_css_class',
    function($classes, $item) {
        /*
         * Переменная $classes содержит
         * Array(
         *   [1] => menu-item
         *   [2] => menu-item-type-post_type
         *   [3] => menu-item-object-page
         *   [4] => menu-item-1911
         * )
         */
        // удаляем все css-классы, которые устанавливает WP
        $classes = [];
        if ($item->current) {
            // добавляем класс active для текущей страницы
            $classes[1] = 'active';
        }
        return $classes;
    },
    10,
    2
);

/*
 * Функция для вывода постраничной навигации с использованием классов
 * Bootstrap 3
 *
 * <nav aria-label="Постраничная навигация">
 *   <ul class="pagination">
 *       <li><a href="#">Предыдущая</a></li>
 *       <li><a href="#">1</a></li>
 *       <li class="active"><a href="#">2</a></li>
 *       <li><a href="#">3</a></li>
 *       <li class="disabled"><a href="#">…</a></li>
 *       <li><a href="#">9</a></li>
 *       <li><a href="#">Следующая</a></li>
 *   </ul>
 * </nav>
 */
function echo_posts_pagination($echo = true) {
    $items = paginate_links(['type' => 'array']);
    if (empty($items)) {
        return null;
    }

    $links = [];
    foreach ($items as $item) {
        $url = $txt = $type = '';
        if (preg_match('~href=("|\')([^"\']+)\1>([^<]+)</a>~', $item, $match)) {
            $url  = $match[2];
            $txt  = $match[3];
            $type = 'link';
        }
        if (preg_match('~page-numbers current("|\')>([^<]+)</span>~', $item, $match)) {
            $txt  = $match[2];
            $type = 'curr';
        }
        if (preg_match('~page-numbers dots("|\')>([^<]+)</span>~', $item, $match)) {
            $txt  = $match[2];
            $type = 'dots';
        }
        $links[] = ['type' => $type, 'url' => $url, 'txt' => $txt];
    }

    if (empty($links)) {
        return null;
    }

    $html = '<nav aria-label="Постраничная навигация">' . "\n";
    $html .= '  <ul class="pagination">' . "\n";
    foreach ($links as $link) {
        if ($link['type'] == 'link') {
            $html .= '    <li>';
            $html .= '<a href="'.$link['url'].'">'.$link['txt'].'</a>';
            $html .= '</li>' . "\n";
        } elseif ($link['type'] == 'curr') {
            $html .= '    <li class="active">';
            $html .= '<span>'.$link['txt'].'</span>';
            $html .= '</li>' . "\n";
        } elseif ($link['type'] == 'dots') {
            $html .= '    <li class="disabled">';
            $html .= '<span>'.$link['txt'].'</span>';
            $html .= '</li>' . "\n";
        }
    }
    $html .= '  </ul>' . "\n";
    $html .= '</nav>' . "\n";

    if ($echo) {
        echo $html;
        return null;
    }
    return $html;
}

/*
 * Подключаем настройщик темы (Theme Customiser)
 */
require get_template_directory() . '/customizer/customizer.php';

Остальные файлы темы:

<?php
/**
 * Файл header.php. Шапка сайта, подключается на всех страницах сайта
 */
?>

<!doctype html>
<html <?php language_attributes(); ?>>
<head>
    <meta charset="<?php bloginfo('charset'); ?>">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <?php wp_head(); ?>
</head>

<body <?php body_class(); ?>>
<header>
    <?php
    /*
     * Фоновое изображение шапки сайта. Если пользователь не загрузил
     * изображение, то оно не будет показано
     */
    $style = '';
    if (has_custom_header()) {
        $style = ' style="background-image: url('.get_header_image().')"';
    }
    ?>
    <div<?= $style; ?>>
        <div class="container">
            <span id="blogname" style="color: #<?php header_textcolor(); ?>">
                <?php bloginfo('name'); ?>
            </span>
            <span id="blogdescription" style="color: #<?php header_textcolor(); ?>">
                <?php bloginfo('description'); ?>
            </span>
        </div>
    </div>
    <nav class="navbar navbar-inverse">
        <div class="container">
            <div class="navbar-header">
                <a href="<?= home_url(); ?>"
                   class="navbar-brand<?= is_front_page() ? ' active': ''; ?>">
                    <span class="glyphicon glyphicon-home"></span>
                </a>
            </div>
            <div class="collapse navbar-collapse" id="main-menu">
                <?php
                wp_nav_menu([
                    'theme_location'  => 'header_menu',
                    'container'       => false,
                    'menu_class'      => 'nav navbar-nav',
                    'menu_id'         => 'main-menu-ul',
                ]);
                ?>
                <div class="navbar-right">
                    <?php if ( ! is_user_logged_in()): ?>
                        <a href="<?= wp_registration_url(); ?>"
                           class="btn btn-default navbar-btn">
                            <?php _e('Регистрация', 'clean'); ?>
                        </a>
                    <?php endif; ?>
                    <?php if ( ! is_user_logged_in()): ?>
                        <a href="<?= wp_login_url(get_permalink()); ?>"
                           class="btn btn-default navbar-btn">
                            <?php _e('Войти', 'clean'); ?>
                        </a>
                    <?php else: ?>
                        <a href="<?= wp_logout_url(get_permalink()); ?>"
                           class="btn btn-default navbar-btn">
                            <?php _e('Выйти', 'clean'); ?>
                        </a>
                    <?php endif; ?>
                </div>
                <?php get_search_form(); ?>
            </div>
        </div>
    </nav>
</header>
<?php
/**
 * Файл footer.php. Подвал сайта, подключается на всех страницах сайта
 */
?>

<footer>
    <div class="container">
        <div class="row">
            <div class="text-center">
                <p>&copy; All Rights Reserved.</p>
            </div>
        </div>
    </div>
</footer>
<?php wp_footer(); ?>
</body>
</html>
<?php
/**
 * Файл sidebar.php. Сайдбар (боковая панель)
 */
?>

<?php if (is_active_sidebar('sidebar_right')): ?>
    <div id="sidebar_right" class="sidebar">
        <?php dynamic_sidebar('sidebar_right'); ?>
    </div>
<?php endif; ?>
<?php
/**
 * Файл front-page.php. Шаблон для главной страницы сайта
 */

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

/*
 * Здесь возможны две ситуации: на главной странице показываются последние
 * записи или на главной показывается статическая страница.
 */
?>

<div id="content">
    <div class="container">
        <div class="row">
            <main class="col-md-9">
                <?php
                if (is_home()) { // показываются последние записи
                    get_template_part('parts/list');
                } else { // показывается статическая страница
                    get_template_part('parts/page');
                }
                ?>
            </main>
            <aside class="col-md-3">
                <?php get_sidebar(); ?>
            </aside>
        </div>
    </div>
</div>

<?php
/*
 * Подключаем подвал сайта
 */
get_footer();
<?php
/**
 * Файл home.php. Шаблон для показа списка последних постов, используется,
 * когда на главной странице показывается статическая страница
 */

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

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

<?php
/*
 * Подключаем подвал сайта
 */
get_footer();
<?php
/**
 * Файл page.php. Шаблон для показа страницы (запись типа page)
 */

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

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

<?php
/*
 * Подключаем подвал сайта
 */
get_footer();
<?php
/**
 * Файл single.php. Шаблон для показа отдельной записи произвольного типа,
 * кроме страниц (тип записи page)
 */

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

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

<?php
/*
 * Подключаем подвал сайта
 */
get_footer();
<?php
/**
 * Файл archive.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'); ?>
            </main>
            <aside class="col-md-3">
                <?php get_sidebar(); ?>
            </aside>
        </div>
    </div>
</div>

<?php
/*
 * Подключаем подвал сайта
 */
get_footer();
<?php
/**
 * Файл search.php. Шаблон для показа результатов поиска по сайту
 */

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

<div id="content">
    <div class="container">
        <div class="row">
            <main class="col-md-9">
                <h1><?php esc_html_e('Поиск по сайту', 'clean'); ?></h1>
                <?php if (!empty(get_search_query())): ?>
                    <h2>
                        <?php
                        esc_html_e('Результаты поиска для: ', 'clean');
                        echo get_search_query();
                        ?>
                    </h2>
                <?php endif; ?>
                <?php get_template_part('parts/list'); ?>
            </main>
            <aside class="col-md-3">
                <?php get_sidebar(); ?>
            </aside>
        </div>
    </div>
</div>

<?php
/*
 * Подключаем подвал сайта
 */
get_footer();
<?php
/**
 * Файл searchform.php. Шаблон формы поиска
 */
?>

<form method="get"
      action="<?= home_url('/'); ?>"
      id="searchform"
      class="navbar-form navbar-right"
      role="search">
    <div class="form-group">
        <input type="text"
               name="s"
               id="s"
               value="<?= get_search_query(); ?>"
               class="form-control"
               placeholder="Поиск по сайту">
    </div>
    <button type="submit" class="btn btn-default">Искать</button>
</form>
<?php
/**
 * Файл comments.php. Шаблон для вывода комментариев
 */
?>

<div id="clean-comments">
    <?php
    if (have_comments()):
        ?>
        <h2><?php esc_html_e('Комментарии', 'clean'); ?></h2>
        <?php the_comments_navigation(); ?>
        <ol class="comment-list">
            <?php
            wp_list_comments([
                'style'      => 'ol',
                'short_ping' => true,
            ]);
            ?>
        </ol>
        <?php
        the_comments_navigation();
        if (!comments_open()):
            ?>
            <p class="no-comments">
                <?php esc_html_e( 'Комментарии закрыты', 'clean' ); ?>
            </p>
            <?php
        endif;
    endif;
    comment_form();
    ?>
</div>
<?php
/**
 * Файл 404.php. Шаблон для страницы 404 Not Found
 */

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

<div id="content">
    <div class="container">
        <div class="row">
            <main id="clean-notfound">
                <h1><?php _e('Страница не найдена', 'clean'); ?></h1>
                <p>
                <?php
                _e(
                    'Запрошенная страница не найдена, попробуйте воспользоваться поиском.',
                    'clean'
                );
                ?>
                </p>

                <?php
                // поиск по сайту
                the_widget('WP_Widget_Search');
                // категории блога
                the_widget('WP_Widget_Categories');
                // свежие записи
                the_widget( 'WP_Widget_Recent_Posts' );
                // сттаницы сайта
                the_widget('WP_Widget_Pages');
                // облако тегов
                the_widget('WP_Widget_Tag_Cloud');
                ?>
            </main>
        </div>
    </div>
</div>

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

Составные части шаблонов, размещаются в поддиректории parts:

<?php
/**
 * Файл parts/list.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 echo_posts_pagination(); ?>
<?php else: ?>
    <p><?php _e('Ничего не найдено', 'clean'); ?></p>
<?php endif; ?>
<?php
/**
 * Файл parts/page.php. Шаблон для страницы сайта (тип записи page)
 */
?>

<?php if (have_posts()): ?>
    <div id="clean-page">
        <?php the_post(); ?>
        <h1><?php the_title(); ?></h1>
        <?php the_content(); ?>
    </div>
<?php endif; ?>
<?php
/**
 * Файл parts/single.php. Шаблон для показа отдельной записи произвольного типа,
 * кроме страниц (тип записи page)
 */
?>

<?php if (have_posts()): ?>
    <div id="clean-single">
        <?php the_post(); ?>
        <h1><?php the_title(); ?></h1>
        <?php the_content(); ?>
        <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 comments_template(); ?>
<?php endif; ?>

Настройки темы через Theme Customizer:

<?php
/*
 * Файл customizer/customizer.php. Когда пользователь изменяет настройки темы в Theme
 * Customizer и наблюдает изменения в окне предварительного просмотра
 */
add_action(
    'customize_register',
    function($wp_customize) {
        /*
         * Обновление окна предварительного просмотра настроек темы без перезагрузки
         */
        $wp_customize->get_setting('blogname')->transport         = 'postMessage';
        $wp_customize->get_setting('blogdescription')->transport  = 'postMessage';
        $wp_customize->get_setting('header_textcolor')->transport = 'postMessage';

        $wp_customize->selective_refresh->add_partial(
            'blogname',
            array(
                'selector'        => '#blogname',
                'render_callback' => function() {
                    bloginfo('name');
                }
            )
        );
        $wp_customize->selective_refresh->add_partial(
            'blogdescription',
            array(
                'selector'        => '#blogdescription',
                'render_callback' => function() {
                    bloginfo('description');
                }
            )
        );
    }
);

/*
 * Подключаем js-файл, который будет обновлять без перезагрузки окно
 * предварительного просмотра сайта при изменении настроек темы
 */
add_action(
    'customize_preview_init',
    function () {
        wp_enqueue_script(
            'clean-customizer',
            get_template_directory_uri() . '/customizer/customizer.js',
            array('customize-preview'),
            null,
            true
        );
    }
);
/**
 * Файл customizer/customizer.js. Обновляет окно предварительного просмотра,
 * когда пользователь изменяет настройки сайта
 */
(function($) {
    // Изменяем цвет заголовка
    wp.customize('header_textcolor', function(value) {
        value.bind(function(to) {
            $('#blogname, #blogdescription').css('color', to);
        });
    });
})(jQuery);

Дополнительно

Поиск: CMS • Web-разработка • WordPress • Шаблон сайта • Тема • Bootstrap

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