Приступая к работе с настройщиком WordPress

Скажем, у вас есть клиент, компания которого настолько велика, что имеет несколько подразделений. Теперь давайте представим, что этот клиент хочет, чтобы каждое подразделение имело свой собственный сайт на выделенном домене. Каждый сайт должен иметь тот же формат, но отличную цветовую гамму. В этом случае лучше использовать настройщик WordPress (также известный, как Theme Customization API), и я хотел бы описать вам простой пример того, как встроить его в тему.

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

Выберем пример

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

Короткие замечания по терминам

Термины Customizer, Theme Modification API и Theme Customization API используется в сообществе WordPress, как тождественные и взаимно заменяющие. Но я думаю, что некоторые различия между ними существуют, и стараюсь их уважать:

  • Customizer - представляет собой раздел интерфейса системы WordPress - /wp-admin/customize.php;
  • Theme Modificaton API - это семейство функций, создающих, считывающих, изменяющих и удаляющих данные, которыми управляет настройщик;
  • Theme Customization API - это семейство функций для удаления и добавления параметров в настройщик.

Создайте тестовый сайт

Убедитесь, что на вашем тестовом сайте установлена последняя версия WordPress - на момент написания статьи это WordPress 4.3.2. На самом деле, для примера нам нужен лишь самый последний WordPress и указанная выше тема, установленная и активированная:

Создайте тестовый сайт

Скриншот из themes.php, показывающий, что наша тема активирована

После того как вы установите тему, войдите в панель администрирования: вы увидите, что в разделе «Дизайн» есть ссылка «Настроить». Этот пункт существует по умолчанию; мне не нужно регистрировать его или объявлять его поддержку.

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

Панель настройщика

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

Вот, как мы можем себе это представить:

Панель
|__ Раздел
|   |__ Параметры и их элементы управления
|   |__ Параметры и их элементы управления
|__ Раздел
|   |__ Параметры и их элементы управления
|   |__ Параметры и их элементы управления
|
Панель
|__ Раздел
|   |__ Параметры и их элементы управления
|   |__ Параметры и их элементы управления
|__ Раздел
|   |__ Параметры и их элементы управления
|   |__ Параметры и их элементы управления

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

Можно сначала зарегистрировать панель, а затем перейти по адресу /wp-admin/customizer.php, чтобы посмотреть, работает ли эта панель. Если вы не обнаружите этой панели, не беспокойтесь: панель не будет выводиться, если в ней еще не зарегистрирован ни один параметр и элемент управления.

Панели

Заходя в панель настройщика, мы сразу же видим слева два раздела меню: «Свойства сайта» и «Body»:

Панели

Скриншот настройщика с двумя разделами меню

«Свойства сайта» добавляются в WordPress по умолчанию, а «Body» создается нашей темой.

Разделы

Если мы нажмем на пункт «Body», то увидим, что он содержит несколько разделов: «Colors» и «Layout Options». Напомню, что моя тема содержит код, который добавляет пункт «Body», разделы «Colors» и «Layout Options»:

Разделы

Скриншот, на котором показаны разделы, добавленные темой в настройщик

Параметры

Если мы зайдем в раздел «Colors», то увидим, что я зарегистрировал несколько параметров и элементов управления, с помощью которых можно изменять цвета темы. По умолчанию я установил значения «Оранжевый» и «Черный», которые введены изначально в соответствующих полях:

Параметры

Скриншот, на котором показано несколько параметров, добавленных темой в настройщик

Несколько слов относительно данных: WordPress сохраняет и извлекает параметры с помощью семейства функций Theme Modification. Мы также можем использовать эти функции, хотя я буду использовать в этой теме только одну функцию get_theme_mod().

Элементы управления

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

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

Элементы WordPress по умолчанию и как их удалить

Давайте вернемся в самый верх панели, а затем зайдем в раздел «Свойства сайта». WordPress добавляет эту панель по умолчанию, пропускает уровень разделов и выводит сразу несколько параметров, таких как «Название сайта» и «Иконка сайта»:

Элементы WordPress по умолчанию и как их удалить

Скриншот параметров, добавляемых WordPress по умолчанию

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

Чтобы удалить компоненты по умолчанию, я подключаюсь к действию customize_register и получаю доступ к объекту $wp_customize:

<?php
class CSST_TMD_Customizer {

  public function __construct() {

    ...

    // Очищаем неиспользуемые компоненты настройщика.
    add_action( 'customize_register', array( $this, 'deregister' ), 999 );

  }

  ...

  /**
   * Удаляем элементы, добавляемые WordPress в настройщик.
   *
   * объект параметра $wp_customize, экземпляр WP_Customize_Manager.
   */
  public function deregister( $wp_customize ) {

    // Удаляем параметр для описания блога.
    $wp_customize -> remove_control( 'blogdescription' );

    // Удаляем раздел для определения статической главной страницы.
    $wp_customize -> remove_section( 'static_front_page' );

    // Удаляем панель для обработки меню навигации.
    $wp_customize -> remove_panel( 'nav_menus' );

  }

?>

Я выбрал для удаления эти элементы, так как они хорошо иллюстрируют иерархическую структуру, о которой я говорил. Какие элементы удалять, я определил, изучив код разметки этой области настройщика. Я использовал атрибут data-customize-setting-link этого элемента управления (вы можете увидеть это на скриншоте, приведенном выше).

Насчет одного момента прошу меня простить: удаление nav_menus выведет предупреждение в режиме отладки. Я не смог найти способ избежать этого.

Определение новых панелей, разделов и параметров

Я определю новые панели, разделы и параметры только один раз в огромном массиве, чтобы остальные моды темы могли использовать его. Это позволит нам добавить эти параметры в настройщик и вывести их с помощью CSS.

Наша тема содержит класс, который называется CSST_TMD_Theme_Mods, в котором я создаю массив панелей, разделов и параметров, добавляемых в настройщик. Давайте разберем пример, в котором мы добавим панель «Body», раздел «Цвета» в эту панель, а затем добавим параметр «Цвет фона» в этот раздел. Другими словами:

Панель: Body
|__ Раздел: Цвета
    |__ Параметр: Цвет фона с элементом управления - панель выбора цвета

Во-первых, панель с именем body определяется следующим образом:

<?php
class CSST_TMD_Theme_Mods {
  /**
   * Определяем наши панели, разделы и параметры.
   * 
   * @return массив. Массив панелей, содержащий разделы, параметры.
   */
  function get_panels() {
    ...

    // Начинаем огромный массив, чтобы определить панели, разделы и параметры.
    $out = array();
    // Определяем панель body.
    $body = array(
      // Заголовок для этой панели в интерфейсе настройщика.
      'title' => esc_html__( 'Body', 'csst_tmd' ),

      // Описание для этой панели в интерфейсе настройщика.
      'description' => esc_html__( 'Моды темы для раздела Body страницы', 'csst_tmd' ),

      // Порядок вывода этой панели в настройщике.
      'priority' => 20,

      // Панель body содержит несколько разделов.
      'sections' => array(),
    );
    $out['body'] = $body; 
Далее добавляем в эту панель раздел с именем colors.
// Определяем раздел colors, который находится в панели body.
    $out['body']['sections']['colors'] = array(
    // Заголовок для этого раздела в интерфейсе настройщика.
    'title' => esc_html__( 'Colors', 'csst_tmd' ),
    // Описание для этого раздела в интерфейсе настройщика.
    'description' => esc_html__( 'Colors for the Page Body', 'csst_tmd' ),

    // Порядок вывода раздела в панели.
    'priority' => 10,

    // Раздел colors содержит ряд параметров.
    'settings' => array(),
  );
?>

Затем в раздел colors добавляется параметр background_color:

<?php
    // Параметр для цвета фона.
    $out['body']['sections']['colors']['settings']['background_color'] = array(
      // Тип элемента управления для этого параметра.
      'type' => 'color',
      // Текст заголовка элемента управления.
      'label' => esc_html__( 'Цвет фона', 'csst_tmd' ),
      // Текст описания для элемента управления.
      'description' => esc_html( 'Цвет фона элемента body, на экранах с альбомной ориентацией с расширением менее 800 пикселей.', 'csst_tmd' ),

      // Порядок вывода элемента управления в разделе.
      'priority' => 10,
      // Значение по умолчанию для этого параметра.
      'default' => '#000000',
      // Функция обратного вызова для проверки данных.
      'sanitize_callback'    => 'sanitize_hex_color',
      'sanitize_js_callback' => 'sanitize_hex_color',
      // Хотим ли мы использовать css этого параметра в TinyMCE?
      'tinymce_css' => FALSE,
      // Является ли этот параметр адаптивным для создания css?
      'css' => array(

        // Этот параметр задается одним правилом css. Здесь мы можем создать несколько правил.
        array(
          // Строка селектора.
          'selector' => 'body',
          // Свойство css.
          'property' => 'background-color',

          // Несколько медиазапросов для этого css.    
          'queries' => array(
            'max-width'   => '800px',
            'orientation' => 'landscape',
          ),
        // Конец правила css. Здесь мы можем начать еще одно правило, чтобы использовать этот параметр для border-color элемента body, или чего-либо другого.         
        ),
      // Конец списка правил css (здесь у нас только одно правило).
      ),

    // Конец параметра.
    );
?>

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

Но вы не обязаны делать это именно таким образом. Вы можете сделать возможным применение правила только в том случае, если в разделе body присутствуют определенные классы. Можно отказаться от использования многомерного массива и объявлять свои параметры по одному.

Если от всех моих циклов у вас закружилась голова, посмотрите прекрасную тему Twenty Sixteen, которая регистрирует параметры «Настройки» вообще без циклов. Я не прибегаю к этой технике, так как считаю, что она не слишком компактная. Например, строка color_scheme присутствует в этом файле в 83 местах.

Передача модов темы в настройщик

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

<?php

class CSST_TMD_Customizer {

  public function __construct() {

    // Регистрируем пользовательские параметры и элементы управления.
    add_action( 'customize_register' , array( $this, 'register' ), 970 );

    ...

  }

  /**
   * Добавляем панели, разделы и параметры в настройщик. 
   * 
   * @param объект $wp_customize - экземпляр класса WP_Customize_Manager.
   */
  public function register( $wp_customize ) {

    // Применяем класс мода нашей темы.
    $theme_mods_class = new CSST_TMD_Theme_Mods;

    // Захватываем панели, разделы и параметры.
    $panels = $theme_mods_class -> get_panels();

    // Для каждой панели...
    foreach ( $panels as $panel_id => $panel ) {

      // Добавляем эту панель в интерфейс.
      $wp_customize -> add_panel(
        $panel_id,
        array(
          'title'       => $panel['title'],
          'description' => $panel['description'],
          'priority'    => $panel['priority'],
        )
      );

      // Для каждого раздела в этой панели, добавляем его в интерфейс и добавляем для него параметры.
      foreach( $panel['sections'] as $section_id => $section ) {

        // Добавляем этот раздел в интерфейс.
        $wp_customize -> add_section(
          $panel_id . '-' . $section_id,
          array(
            'title'       => $section['title'],
            'description' => $section['description'],
            'priority'    => $section['priority'],
            'panel'       => $panel_id,
          )
        );

        // Для каждого параметра в этом разделе, добавляем его в интерфейс.
        foreach( $section['settings'] as $setting_id => $setting ) {

          // Начинаем построение массива аргументов для добавления параметров.
          $setting_args = array(
            'default'              => $setting['default'],
            'sanitize_callback'    => $setting['sanitize_callback'],
            'sanitize_js_callback' => $setting['sanitize_js_callback'],
          );

          // Регистрируем параметр.
          $wp_customize -> add_setting(
            $panel_id . '-' . $section_id . '-' . $setting_id,
            $setting_args
          );

          // Начинаем построение массива аргументов для добавления элементов управления.
          $control_args = array(
            'label'       => $setting['label'],
            'section'     => $panel_id . '-' . $section_id,
            'type'        => $setting['type'],
            'description' => $setting['description'],
          );

          // Параметр типа 'color' специальный тип элемента управления.
          if( $setting['type'] == 'color' ) {

            $wp_customize -> add_control(

              // Он поддерживается в WordPress по умолчанию. Это панель выбора цвета.
              new WP_Customize_Color_Control(
                $wp_customize,
                $panel_id . '-' . $section_id . '-' . $setting_id,
                $control_args
              )

            );

          // Иначе WordPress будет использовать элемент управления по умолчанию.
          } else {

            $wp_customize -> add_control(
              $panel_id . '-' . $section_id . '-' . $setting_id,
              $control_args
            );

          }

        // Конец этого параметра.
        }

      // Конец этого раздела.
      }

    // Конец этой панели.
    }

  }

?>

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

Все это проще переварить, если вы посмотрите исходный файл. Ознакомьтесь с документацией кодекса WordPress по функциям, которые я использую для взаимодействия с $wp_customize, таким как метод add_setting().

Поговорим о функции предварительного просмотра

Панели предварительного просмотра позволяет быстро выбрать и просмотреть изменения для темы:

Поговорим о функции предварительного просмотра

Чтобы внешний вид изменялся так быстро, нужно включить "предварительный просмотр". Эта часть не включена в нашу тему, и я ее обычно не использую. Существует два распространенных способа подключить предварительный просмотр:

1: Встроенные стили для элемента

Наиболее часто документируемый способ реализации предварительного просмотра. Для изменения CSS выбранных элементов HTML использует JavaScript:

<script>

  // Обновление цвета ссылки по умолчанию.
  wp.customize( 'mytheme_options[link_color]', function( value ) {
    value.bind( function( newval ) {
      $( 'a' ).css( 'color', newval );
    } );
  } );

  // Обновление цвета ссылки в сайдбаре.
  wp.customize( 'mytheme_options[sidebar_link_color]', function( value ) {
    value.bind( function( newval ) {
      $( '.sidebar a' ).css( 'color', newval );
    } );
  } );

</script>

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

Еще одна проблема этого подхода заключается в медиазапросах. Напомню, что диапазон применения модов нашей темы распространяется на конкретные медиазапросы. Чтобы передать эту логику через JS потребовалось бы много работы.

2: Встроенные блоки стилей

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

Я попытался разобраться в этом и перекодировать этот ресурс во что-то, что бы я мог действительно понять и поддерживать самостоятельно. И у меня возникли трудности. Здесь я нашел некоторые действия, но на самом деле этот тикет не помог сделать так, чтобы мне нравилось, как выглядит предварительный просмотр.

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

Стили для Front End

Мы сделали все, чтобы создать интерфейс настройщика, и он работает. Можно использовать эти значения в любом месте: CSS для wp_head(), классах раздела body, редакторе TinyMCE. Мы также можем передать значения в JS.

Давайте используем их для вывода CSS. У нас уже есть несколько параметров для управления цветом шрифта и цветом фона для элемента body, так что мы можем начать с этого.

Тема содержит класс CSST_TMD_Inline_Styles, который обрабатывает в цикле все параметры и выводит соответствующий CSS-код с помощью функции add_inline_style().

Это означает, что пользовательские стили добавляются в очередь загрузки в зависимости от стилей темы. Вот как это выглядит в действии:

<?php

class CSST_TMD_Inline_Styles {

  public function __construct( $output_for = 'front_end' ) {

    // Добавляем наши стили в front end блога.
    add_action( 'wp_enqueue_scripts', array( $this, 'front_end_styles' ) );

    ...

  }

  /**
   * Вставляем стили настройщика в раздел <head>, когда бы ни вызывалась основная таблица стилей.
   */
  public function front_end_styles() {

    // Захватываем стили, которые относятся к front end, но не оборачиваем их в тег стиля.
    $styles = $this -> get_inline_styles( 'unwrapped', 'front_end' );

    // Присоединяем стили настройщика.
    wp_add_inline_style( CSST_TMD, $styles );

  }

  ...

?>

Мы вызываем метод $this -> get_inline_styles(). Этот метод отвечает за обработку в цикле всех настроек настройщика и вывод гигантского блока CSS:

<?php

class CSST_TMD_Inline_Styles {

  ...

  /**
   * Обработка в цикле модов темы и построение строк правил CSS.
   * 
   * @param строка $wrapped.  Оборачивать или нет стили в теги стилей. Принимает значение 'wrapped' или 'unwrapped'.
   * @param строка $output_for Контекст для стилей. Принимает значение 'front_end' или 'tinymce'.
   * @return строка CSS, оборачивать или нет в тег стиля.
   */
  public function get_inline_styles($wrapped = 'wrapped', $output_for = 'front_end' ) {

    // Здесь содержатся все стили настройщика.
    $out = '';

    ...

    // Для каждого параметра...
    foreach( $settings as $setting_id => $setting ) {

      // Захватываем css для этого параметра.
      $css_rules = $setting['css'];

      // Захватываем текущее значение для этого параметра.
      $value = $setting['value'];

      // Для каждого правила css...
      foreach( $css_rules as $css_rule ) {

        // Селектор css.
        $selector = $css_rule['selector'];

        // Свойство css.
        $property = $css_rule['property'];

        // Задаем из этого правило CSS.
        $rule_string = "$selector { $property : $value ; }";

        // Содержит ли это правило медиазапросы?
        if( isset( $css_rule['queries'] ) ) {

          ...

          foreach( $queries as $query_key => $query_value ) {

            ...

            // Добавляем ключ и значение меди-запроса.
            $query .= "( $query_key : $query_value )";

            // Если это не последний запрос, добавляем оператор "and".
            if( $i < $query_count ) {
              $query .= ' and ';
            }

            ...

          }

          // Оборачиваем строку правила в медиазапрос.
          $rule_string = " @media $query { $rule_string } ";

        }

        // Добавляем правило, которое может быть обернуто в медиазапрос, на вывод.
        $out .= $rule_string;

      }

    }

    ...

    return $out;

  }

}

?>

Блок встроенных стилей построен. На данный момент это одна строка, в которой каждый селектор CSS и каждое свойство CSS определено в массиве параметров. Значение CSS - это ранее сохраненные через настройщик данные, которые мы извлекаем.

Другие применения параметров настройщика

Приведенный выше пример является самым важным вариантом применения кастомайзера. Но далеко не единственным. У нас также есть класс, CSST_TMD_Body_Classes, в котором мы добавляем каждый параметр как класс раздела body - и в wp-admin, и в front end. Это может быть полезно, если у нас есть параметр для смещения сайдбара вправо или влево.

Класс CSST_TMD_Tiny_MCE служит для определения стилей редактора записей в wp-admin, поэтому в нем будут использоваться те же цвета, что заданы через параметр настройщика.

Ресурсы и следующие шаги

Я настоятельно рекомендую вам следить за обсуждениями темы настройщика в блоге Make. В недавней статье там отмечается, что производительность front-end - это проблемная область настройщика. Я согласен с этим, но производительность в Chrome и Firefox может быть существенно улучшена. Также в статье упоминается о возможности использования ревизий для настройщика, подобно ревизиям для записей.

Я видел несколько сборников пользовательских элементов управления, размещенных на GitHub. Этот содержит несколько полезных и сложных элементов управления. А вот еще один.

Если вы можете выполнить все, что было описано в данной статье, вам логично будет предпринять дальнейшие шаги:

  • Добавить другие параметры в массив;
  • Сделать синтаксис медиазапросов более надежным, чтобы вы могли регистрировать медиазапросы с помощью специфических логических операторов;
  • Улучшить шаблоны JavaScript для пользовательских типов элементов управления.

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