- 12.05.15
- 21:44
- 1323
- 0
Задача: сайдбары темы должны поддерживать несколько вариантов оформления виджетов (см. рис.) Соответствие вариантов виджетам задано в виде двухуровневого хеша:
id сайдбара => id_base типа виджета => вариант оформления
.
Т. е. режим отображения виджета следует получать так:
1 2 |
$mode_id = $a_widget_modes[$sidebar_id][$id_base]; $mode = $AVAILABLE_MODES[$mode_id]; |
Все режимы являются частью шаблонов (содержат фрагменты вёрстки) и не имеют настраиваемых параметров. Поэтому они заданы в виде массива $AVAILABLE_MODES
прямо в коде темы.
Массив соответствия вариантов виджетам $a_widget_modes
является настраиваемым и каким-либо образом задаётся администратором.
Решение задачи сводится к коррекции передаваемых виджету параметров сайдбара before_widget
, after_widget
, before_title
и after_title
.
Корректировка проводится фильтром 'dynamic_sidebar_params'
. Фильтруемым значением является числовой массив, содержащий два элемента:
[0]
— параметры сайдбара, идентификатор и имя виджета;[1]
— хеш с номером виджета.
Структура фильтруемого значения (обозначим его как $params
) такова:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Array ( [0] => Array ( [name] => Правый сайдбар [id] => right-sidebar [description] => [class] => [before_widget] => <div id="sample_widget-4" class="widget-wrap sample_widget"> [after_widget] => </div></div> [before_title] => <div class="widget-title"><p> [after_title] => </p></div><div class="widget-body"> [widget_id] => sample_widget-4 [widget_name] => Sample Widget ) [1] => Array ( [number] => 4 ) ) |
Здесь примечательны id
, widget_id
и before_widget
.
Параметр id
— идентификатор сайдбара. Это первый ключ в хеше $a_widget_modes
.
Второй ключ можно извлечь из идентификатора виджета — параметра widget_id
— который всегда имеет вид "{$id_base}-{$number}"
:
1 |
$id_base = preg_replace('/-\d+$/', '', $params[0]['widget_id']); |
Параметр before_widget
является уже обработанной форматной строкой: при регистрации сайдбара он был задан как '<div id="%1$s" class="widget-wrap %2$s">'
. Идентификатор и классы (кроме 'widget-wrap'
) должны быть сохранены. Поэтому заменить его на заранее заготовленную строку нельзя, и если вёрстка виджета определяется к.-л. дополнительным классом этого блока, его потребуется изменять регулярным выражением:
1 2 3 4 5 |
$params[0]['before_widget'] = preg_replace( '/class="[^"]*(?=")/', '$0 ' . $new_classes, // добавляемые классы, строка $params[0]['before_widget'] ); |
Если добавления класса окажется не достаточно, то требуемую строку удобнее будет формировать вызовом vsprintf($format, $args)
. Для этого параметр before_widget
при регистрации сайдбара следует задать в виде '%1$s|%2$s'
, тогда аргумент $args
можно получить с помощью explode
. Форматные строки $format
находятся в $AVAILABLE_MODES
, их вид аналогичен параметрам before_widget
в вызовах register_sidebar
для обычных сайдбаров. Фильтрация в этом случае примет такой вид:
1 2 3 4 5 6 7 |
$a_modes = $AVAILABLE_MODES[$sidebar_id]; // варианты отображения данного сайдбара $mode_id = $a_widget_modes[$sidebar_id][$id_base]; // идентификатор варианта $format = $a_modes[$mode_id]['before_widget_fmt']; // параметр задан в виде форматной строки $params[0]['before_widget'] = vsprintf( $format, explode('|', $params[0]['before_widget']) // даёт массив array($id, $classes) ); |
Коррекция этого параметра должна происходить для всех без исключения виджетов данного сайдбара. Поэтому нужен «вариант оформления по умолчанию». Назовём его 'default'
и будем дополнять его параметрами все остальные варианты. Он также относится к сайдбару и определяется его идентификатором. Получение параметров варианта усложнится:
1 2 3 4 5 6 7 8 9 10 11 |
$a_modes = $AVAILABLE_MODES[$sidebar_id]; $mode = isset($a_widget_modes[$sidebar_id][$id_base]) ? $mode = $a_modes[$a_widget_modes[$sidebar_id][$id_base]] : array(); if (!empty($a_modes['default'])) $mode = wp_parse_args($mode, $a_modes['default']); $format = $mode['before_widget_fmt']; // параметр задан в виде форматной строки ... |
Остальные параметры сайдбара — before_title
, after_title
, after_widget
— просто строки, значения им можно просто присвоить.
Костыль будет удобен для сайдбаров с одинаковыми параметрами. Таковыми обычно являются части горизонтальных сайдбаров (напр., в футере). Чтобы не дублировать описания их режимов в $AVAILABLE_MODES
, надо ввести массив соответствия такого вида:
1 2 3 4 5 6 7 8 9 |
$IDENTICAL_SIDEBARS = array( // ID сайдбара ID оформления виджетов сайдбара 'footer-1' => 'footer', 'footer-2' => 'footer', ... 'footer-N' => 'footer', 'another-sidebar-1' => 'another-sidebar', ... ); |
Перед получением $mode_id
идентификатор $sidebar_id
должен быть скорректирован:
1 2 |
if (isset($IDENTICAL_SIDEBARS[$sidebar_id])) $sidebar_id = $IDENTICAL_SIDEBARS[$sidebar_id]; |
Такие идентификаторы — значения $IDENTICAL_SIDEBARS
— следует использовать вместо идентификаторов одинаковых сайдбаров при написании $AVAILABLE_MODES
и при формировании $a_widget_modes
.
Результат выглядит так (всё лишнее вырезано):
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
class MyTheme { ... public function __construct () { ... // регистрация сайдбаров проводится при инициализации виджетов: add_action('widgets_init', array($this, 'register_sidebars')); // фильтрация параметров выдаваемых виджетов: add_filter('dynamic_sidebar_params', array($this, 'dynamic_sidebar_params')); ... } public function register_sidebars () { register_sidebar(array( // XXX антипаттерн: локализуемая строка должна быть англоязычной: 'name' => __('Правый сайдбар', 'my_text_domain'), 'id' => 'right-sidebar', 'before_title' => '<div class="rsw-title"><span>', 'after_title' => '</span></div>', 'before_widget' => '%1$s|%2$s', 'after_widget' => '', )); // 4 одинаковых сайдбара в футере: footer-1, ..., footer-4: register_sidebar(array( 'name' => __('Футер, область #1', 'my_text_domain'), 'id' => 'footer-1', 'before_title' => '<p class="bwa-title"><span>', 'after_title' => '</span></p><div class="bwa-body">', 'before_widget' => '<div id="%1$s" class="bwa-widget %2$s">', 'after_widget' => '</div></div>', )); } // сайдбары, виджеты в которых обрабатываются одинаково: private static $IDENTICAL_SIDEBARS = array ( 'footer-1' => 'footer', 'footer-2' => 'footer', 'footer-3' => 'footer', 'footer-4' => 'footer', ); private static $AVAILABLE_MODES = array( // данная структура определяется вёрсткой, поэтому не настраивается. 'right-sidebar' => array( 'default' => array( 'before_widget_format' => '<div id="%1$s" class="widget-wrap %2$s">', 'after_widget' => '</div>', ), 'simple' => array( 'before_widget_classes' => 'simple', ), 'inbox' => array( 'before_widget_format' => '<div id="%1$s" class="widget-wrap inbox %2$s"><div>', 'after_widget' => '</div></div>', ), 'bordered' => array( 'before_widget_format' => '<div id="%1$s" class="widget-wrap inbox bordered %2$s"><div>', 'after_widget' => '</div></div>', ), ), 'footer' => array( 'simple' => array ( 'before_widget_classes' => 'simple', 'after_title' => '</span></p><div class="footer-widget-body thin-border">', ), 'bordered' => array( 'before_widget_classes' => 'bordered', // XXX антипаттерн: after_title не должен бы влиять на after_widget; // этого лучше избегать; 'after_title' => '</span></p><div class="footer-widget-body thick-border"><div>', 'after_widget' => '</div></div></div>' ), ), ); public static $a_widget_modes = array( /* эта структура должна бы располагаться в опциях, но реализация её настройки - отдельная задача, которую пока можно отложить. */ 'right-sidebar' => array( 'pages' => 'simple', 'calendar' => 'inbox', 'text' => 'bordered', 'my-recent-posts' => 'simple', ), 'footer' => array( 'my-tags' => 'simple', 'my-currencies' => 'simple', 'my-categories' => 'bordered', ), ); private static $a_sb_params = array('before_title', 'after_title', 'after_widget'); public function dynamic_sidebar_params ($params) { $sidebar_id = $params[0]['id']; // идентификатор сайдбара if (isset(self::$IDENTICAL_SIDEBARS[$sidebar_id])) // поправка для одинаковых сайдбаров $sidebar_id = self::$IDENTICAL_SIDEBARS[$sidebar_id]; if (!isset(self::$AVAILABLE_MODES[$sidebar_id])) // для данного сайдбара вариантов оформления нет return $params; // массив режимов отображения виджетов для данного сайдбара $a_modes = self::$AVAILABLE_MODES[$sidebar_id]; $id_base = preg_replace('/-\d+$/', '', $params[0]['widget_id']); /* если режим для виджетов данного типа не установлен, следует использовать пустой массив - он будет дополнен умолчаниями; */ $mode = isset(self::$a_widget_modes[$sidebar_id][$id_base]) ? $mode = $a_modes[self::$a_widget_modes[$sidebar_id][$id_base]] : array(); if (isset($a_modes['default'])) // если есть умолчания - дополнить ими режим $mode = wp_parse_args($mode, $a_modes['default']); if (isset($mode['before_widget_format'])) { // здесь считаем, что before_widget задан как '%1$s|%2$s' $params[0]['before_widget'] = vsprintf( $mode['before_widget_format'], explode('|', $params[0]['before_widget']) ); } if (isset($mode['before_widget_classes'])) { $params[0]['before_widget'] = preg_replace( '/class="[^"]*(?=")/', '$0 ' . $mode['before_widget_classes'], $params[0]['before_widget'] ); } foreach (self::$a_sb_params as $param) { if (isset($mode[$param])) { $params[0][$param] = $mode[$param]; } } return $params; } ... } |
Замечания:
- строка 15: локализуемые строки должны быть англоязычными, потому что пользователи с неподдерживаемыми локалями будут читать исходный текст;
- строки 71-72: открывать блоки в
after_title
нежелательно, если это приводит к несбалансированнымbefore_widget
иafter_widget
: так как при пустом заголовке окружающий его код выводить не принято, блок виджета может оказаться некорректным и нарушить отображение страницы. Решение проблем, возникающих в подобных случаях, рассматривается в статье «Темы WordPress и пустые заголовки виджетов». - строки 122-128: параметр
before_widget_format
должен задаваться только для сайдбаров сbefore_widget
равным'%1$s|%2$s'
, иначе сформируется тарабарщина; - строки 130-136: содержимое параметра
before_widget_classes
добавляется к первому атрибуту class, найденному вbefore_widget
. Т. е. в вариант вроде1<div class="outer"><div id="%1$s" class="widget %2$s">...