- 11.05.15
- 16:17
- 1737
- 0
Виджеты — самостоятельные фрагменты страницы, содержимое которых зависит от их типа и настроек. Настройки каждого виджета индивидуальны, набор составляющих их параметров определяется его типом.
Размещаются виджеты в отведённых для этого областях страницы — сайдбарах, называемых также боковыми панелями. Сайдбары принадлежат теме. Размещение на них виджетов происходит на экране «Внешний вид > Виджеты» (/wp-admin/widgets.php
).
Конструкция
Каждый тип виджетов представлен собственным классом. Все классы виджетов происходят от WP_Widget
, поэтому также могут называться подклассами или субклассами виджетов.
Чтобы не возникало проблем с терминологией, заметим, что класс/подкласс/субкласс виджетов правильнее было бы называть «классом типа виджетов», т. к. WordPress создаёт лишь один его экземпляр и использует его методы для обработки всех виджетов данного типа. Но такое длинное название неудобно. Поэтому здесь используется термин «класс виджетов», обозначающий как PHP-класс, так и реализуемый им тип — конкретное значение понятно из контекста.
Каждый класс виджетов содержит конструктор и три метода:
widget
— производит вывод виджета;form
— выдаёт форму настройки;update
— осуществляет контроль корректности применяемых настроек.
Метод widget
обязателен.
Также в составе класса может присутствовать массив значений по умолчанию параметров плагина — $defaults
— если параметров много и их исходные значения различны.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class My_Widget_Class extends WP_Widget { var $defaults = array( ... ); public function __construct () { ... } public function widget ($args, $instance) { ... } public function form ($instance) { ... } public function update ($new_instance, $old_instance) { ... } } |
Конструктор
Конструктор __construct
(рассматриваем PHP 5+) должен вызывать конструктор родителя:
1 2 3 4 |
parent::__construct('id-base', 'Название виджета', array( 'classname' => 'myplugin_widget_my_widget', 'description' => 'Описание', )); |
Аргументы ему передаются следующие:
- арг. 1 —
'id-base'
— основа идентификаторов виджетов данного типа, по-сути является идентификатором самого типа. Должен быть уникальным. Перед использованием преобразуется в малый регистр. Если пуст, в качестве значения принимается имя класса в малом регистре и без фрагмента/^(wp_)?widget_/
, если таковой имеется. Хранится в поле$id_base
. Не изменяется.Идентификаторы виджетов формируются добавлением к
$id_base
их номеров:1$widget_id = "{$id_base}-{$number}";Номером виджета является целое число не меньше 2. Идентификаторы виджетов также являются идентификаторами содержащих их блоков при размещении в сайдбарах.
- арг. 2 — читаемое название виджета — будет использоваться, напр., на экране управления виджетами.
арг. 3 (опционален) — массив параметров, связанных с отображением и описанием виджета. На данный момент поддерживаются только два ключа:
'description'
— описание виджета;'classname'
— класс, добавляемый к блоку виджета; по умолчанию используется"widget_{$id_base}"
.
- арг. 4 (опционален) — массив параметров управления. Может содержать параметр
'width'
— ширина формы настройки в единицах CSS, использовать который не рекомендуется. Вариант сarray('width' => '500px')
представлен на рисунке.
Конструктор каждого типа виджетов вызывается только один раз — используется лишь один экземпляр класса — поэтому в него можно поместить регистрацию хуков, обработчики которых удобнее реализовать в виде методов класса. Обработчик задаётся в виде array($this, 'method')
:
1 2 |
add_action('action_hook', array($this, 'some_method'), ...); add_filter('filter_hook', array($this, 'some_filter'), ...); |
В качестве примера можно привести такие штатные виджеты, как «Свежие записи» (класс WP_Widget_Recent_Posts
) и «Свежие комментарии» (класс WP_Widget_Recent_Comments
): они кешируют свой контент и пользуются некоторыми хуками для сброса кеша, что необходимо при любых изменениях, способных повлиять на отображаемую информацию. Код виджетов расположен в /wp-includes/default-widgets.php
.
Настройка виджета: методы form
и update
Каждый экземпляр виджета обладает собственным экземпляром настроек. Процесс их изменения следующий:
- метод
form
отображает форму с текущими настройками; - администратор редактирует поля формы;
- WordPress получает отредактированные данные и передаёт их методу
update
; - данные, возвращаемые методом
update
, сохраняются в БД.
Методу form
массив настроек передаётся в качестве аргумента. Сразу после создания виджета настройки пусты и их следует инициализировать некоторыми значениями по умолчанию. Обычно это происходит так:
1 |
$instance = wp_parse_args((array)$instance, $this->defaults); |
Функция wp_parse_args
возвращает массив, содержащий все пары «ключ-значение» из первого аргумента и те пары из второго, ключи которых в первом отсутствуют.
Такая инициализация позволяет не корректировать настройки уже существующих виджетов при добавлении новых параметров: их исходные значения будут добавлены к «устаревшему» набору $instance
.
Метод form
опционален, и виджеты без настроек могут его не реализовывать. Но обычно все они имеют настраиваемый заголовок, поэтому им требуется как метод form
, так и метод контроля новых значений update
, чистящий заголовок от потенциально опасного кода.
Метод update
получает два аргумента — массив новых настроек и массив старых, и должен вернуть массив «корректных» — проверенных и поправленных. Этот результат будет сохранён в БД. Метод опционален, его реализация в WP_Widget
просто возвращает первый аргумент — новые настройки — без к.-л. контроля.
Чтобы WordPress мог соотносить введённые данные с параметрами настроек, имена полей ввода должны формироваться методом get_field_name('имя_параметра')
:
1 |
<input ... name="<?php echo $this->get_field_name('field'); ?>" ... /> |
Метод унаследован от WP_Widget
. При получении ответа формы WordPress выбирает из него значения и создаёт набор отредактированных настроек. Этот набор передаётся в update
первым аргументом.
Настроечные формы виджетов одного типа одинаковы. Чтобы идентификаторы полей ввода в них всегда оставались уникальными, их формируют методом get_field_id('имя_параметра')
, также унаследованным от WP_Widget
:
1 |
<input ... id="<?php echo $this->get_field_id('field'); ?>" ... /> |
Если параметр $instance['field']
— хеш или числовой массив, а редактируемое значение — его элемент, то аргумент этих функций имеет вид 'field[key]'
.
Вот методы update
и form
штатного виджета WP_Widget_Calendar
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// добавлены переносы и комментарии вида "спасибо, кеп" public function update ($new_instance, $old_instance) { // за основу принимаются старые настройки: $instance = $old_instance; // из нового заголовка удаляются html-теги: $instance['title'] = strip_tags($new_instance['title']); return $instance; // этот хеш будет сохранён в БД. } public function form ($instance) { // значения по умолчанию на случай, когда $instance пуст: $instance = wp_parse_args((array)$instance, array('title' => '')); // удаление тегов из значения заголовка перед выводом графы ввода: $title = strip_tags($instance['title']); ?> <p> <label for="<?php echo $this->get_field_id('title'); ?>"> <?php _e('Title:'); ?> </label> <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo esc_attr($title); ?>" /> </p> <?php } |
Замечание: при каждой работе с параметром из него удаляются HTML- и PHP-теги (строки 6, 15), а перед выводом на форму значение экранируется как атрибут тега (строка 24).
Вывод виджета: метод widget
Вывод виджета на страницу выполняется методом widget
. В аргументах он получает два массива: с настройками виджета и с параметрами сайдбара, за которым этот виджет закреплён.
Сайдбары являются частью темы, и виджеты в каждом из них свёрстаны по-своему. Фрагменты кода этой вёрстки, необходимые для формирования виджетов, передаются в параметрах сайдбара. Всего таких фрагментов — две пары:
- фрагменты, обрамляющие весь виджет:
before_widget
иafter_widget
; - фрагменты, обрамляющие непустой заголовок виджета:
before_title
иafter_title
.
Вывод метода widget должен иметь такую структуру:
1 2 3 4 5 6 |
before_widget before_title заголовок after_title контент виджета after_widget |
Замечание: отступы добавлены только для наглядности — ни отступов, ни переносов быть не должно, т. к. в некоторых специфичных случаях они могут оказать влияние на вёрстку (когда, напр., промежуток между отступами является содержимым блока, стилизованного с использованием псевдокласса :empty
).
Параметры сайдбара передаются в первом аргументе:
1 |
public function widget ($args, $instance) { ... } |
Второй аргумент — массив настроек виджета — перед применением должен быть скорректирован также, как и в методе form
— на случай, если он окажется устаревшим, пустым или некорректным. Последний вариант возможен, когда вывод осуществляется при помощи the_widget
.
С выводом заголовка виджета связаны несколько соглашений:
- перед выводом заголовок фильтруется хуком
'widget_title'
:
1$title = apply_filters('widget_title', $title, $instance, $this->id_base); - после фильтрации ни каких изменений в заголовок не вносится;
- фрагменты
before_title
иafter_title
выводятся только с непустым заголовком:
123if ($title) {echo $args['before_title'], $title, $args['after_title'];}
Некоторые виджеты заменяют пустой заголовок некоторым стандартным (к таким относятся, напр., штатные виджеты «Облако меток», «Последние записи», «Последние комментарии» и др.) Подобную замену следует проводить перед фильтрацией, а вывод осуществлять по тому же условию, т. к. заголовок может быть очищен фильтром.
В ряде случаев вёрстка такова, что before_title
и after_title
должны выводиться и для виджетов с пустыми заголовками. В этом случае тема регистрирует фильтр 'widget_title'
, заменяющий пустую строку пробелом. Более подробно — в статье Темы WordPress и пустые заголовки виджетов.
Пример — метод widget
класса WP_Widget_Calendar
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// добавлены переносы и комментарии public function widget ($args, $instance) { // здесь $instance не корректируется, т.к. содержит // один единственный параметр - title; $title = apply_filters('widget_title', // а сам параметр корректируется прямо перед фильтрацией: empty($instance['title']) ? '' : $instance['title'], $instance, $this->id_base ); echo $args['before_widget']; if ($title) { echo $args['before_title'] . $title . $args['after_title']; } // контент виджета: echo '<div id="calendar_wrap">'; get_calendar(); echo '</div>'; // закрывающий фрагмент: echo $args['after_widget']; } |
Замечания по примеру:
- виджет обладает единственным настраиваемым параметром — заголовком, поэтому настроек по умолчанию класс не имеет и коррекции
$instance
не проводит. Непосредственно корректируется сам параметр в строке 9. - заголовок и связанные с ним фрагменты выводятся одним оператором и в рамках одного условия (строки 15-17), что удобно при внесении изменений.
Регистрация класса виджетов
Класс виджетов в WordPress регистрируется вызовом register_widget('Class_Name')
, аргументом которого является имя этого класса. Вызов должен происходить по хуку 'widgets_init'
с приоритетом не выше 99. Регистрация более поздняя будет проигнорирована.
1 2 3 |
add_action('widgets_init', function () { register_widget('My_Widget_Class'); }); |
Резюме
- виджеты — самостоятельные фрагменты страницы, размещаемые в сайдбарах;
- типы виджетов определяются в виде классов, производных от
WP_Widget
; - конструктор класса виджетов должен вызывать конструктор родителя, передавая ему
id_base
, название и массив с описанием и классом блока виджета; - три метода должны быть определены:
public function widget($args, $instance);
public function form($instance);
public function update($new_instance, $old_instance)
- метод
form
должен формировать идентификаторы и имена полей ввода с помощьюget_field_id('field')
иget_field_name('field')
; - метод
update
должен возвращать корректную версию настроек; - в методах
form
иwidget
массив$instance
следует дополнять массивом исходных значений; - в методе
widget
- заголовок пропускается через фильтр
widget_title
; - обрамление пустого заголовка не выводится;
- информация выдаётся в таком порядке:
12345echo $args['before_widget'];if ($title)echo $args[before_title], $title, $args['after_title'];echo $content; // содержимое виджетаecho $args['after_widget'];
- заголовок пропускается через фильтр
- регистрируется класс виджетов вызовом
register_widget('Class_Name')
по хуку'widgets_init'
с приоритетом не выше 99.