Программное добавление виджетов в сайдбары
Я хотел бы программно добавить виджеты в мои два сайдбара. Я не смог найти официальный способ сделать это?
Я начал искать в базе данных. Я обнаружил, что за размещение виджетов в сайдбарах отвечает опция 'sidebars_widgets'. При просмотре опций к названиям виджетов добавляется число, например: widget_name-6. Откуда берется это число?
Есть какие-нибудь идеи, как решить эту задачу?
Когда я начинал этот ответ, он должен был быть просто небольшой заметкой. Что ж, у меня не получилось! Извините! Оставайтесь со мной, в глубине текста спрятано кое-что интересное...
Как виджеты WordPress хранятся
Список виджетов хранится в опции с именем 'sidebars_widgets'
. Результат var_export()
может выглядеть примерно так:
array (
'wp_inactive_widgets' =>
array (
),
'top-widget' =>
array (
),
'bottom-widget' =>
array (
),
'array_version' => 3,
)
Игнорируйте 'wp_inactive_widgets'
и 'array_version'
. Нам не нужно о них беспокоиться.
Остальные ключи — это идентификаторы зарегистрированных областей виджетов. В данном случае области могли быть зарегистрированы таким кодом:
// Регистрируем две области виджетов.
$sidebars = array ( 'a' => 'top-widget', 'b' => 'bottom-widget' );
foreach ( $sidebars as $sidebar )
{
register_sidebar(
array (
'name' => $sidebar,
'id' => $sidebar,
'before_widget' => '',
'after_widget' => ''
)
);
}
По умолчанию после регистрации области виджетов пусты. Конечно же.
Для каждого зарегистрированного класса виджета создается отдельная опция, содержащая все необходимые настройки. Имя опции начинается с префикса widget_
. Чтобы получить настройки всех активных RSS-виджетов, нужно обратиться к...
get_option( 'widget_rss' );
Возможный вывод:
array (
2 =>
array (
'title' => 'WordPress Stack Exchange',
'url' => 'http://wordpress.stackexchange.com/feeds',
'link' => 'http://wordpress.stackexchange.com/questions',
'items' => 5,
'show_summary' => 1,
'show_author' => 0,
'show_date' => 0,
),
)
Обратите внимание на число 2. Параметры для нескольких экземпляров виджета хранятся в одной опции и упорядочены по номерам.
Чтобы увидеть, какие классы виджетов уже известны WordPress, перейдите в wp-admin/options.php
и прокрутите вниз до раздела, похожего на этот:
Да, это сериализованные данные. Нет, вы не сможете их прочитать здесь. Но не беспокойтесь, вам и не нужно.
Демо-виджет
Чтобы лучше проиллюстрировать внутреннюю работу, я написал очень простой демо-виджет:
/**
* Очень простой виджет.
*/
class T5_Demo_Widget extends WP_Widget
{
public function __construct()
{ // id_base , видимое имя
parent::__construct( 't5_demo_widget', 'T5 Demo Widget' );
}
public function widget( $args, $instance )
{
echo $args['before_widget'], wpautop( $instance['text'] ), $args['after_widget'];
}
public function form( $instance )
{
$text = isset ( $instance['text'] )
? esc_textarea( $instance['text'] ) : '';
printf(
'<textarea class="widefat" rows="7" cols="20" id="%1$s" name="%2$s">%3$s</textarea>',
$this->get_field_id( 'text' ),
$this->get_field_name( 'text' ),
$text
);
}
}
Обратите внимание на конструктор: 't5_demo_widget'
— это $id_base
, идентификатор этого виджета. Как видно на скриншоте, его параметры хранятся в опции widget_t5_demo_widget
. Все ваши пользовательские виджеты будут обрабатываться аналогично. Вам не нужно угадывать имя. И поскольку вы написали свои виджеты (вероятно), вы знаете все аргументы из параметров $instance
вашего класса.
Основы темы
Сначала нужно зарегистрировать области виджетов и пользовательский виджет. Подходящий экшен для этого легко запомнить: 'widgets_init'
. Поместите всё в контейнер — класс или функцию. Для простоты я использую функцию с именем t5_default_widget_demo()
.
Весь следующий код добавляется в functions.php
. Класс T5_Demo_Widget
должен быть уже загружен. Я просто поместил его в тот же файл...
add_action( 'widgets_init', 't5_default_widget_demo' );
function t5_default_widget_demo()
{
// Регистрируем наш виджет.
register_widget( 'T5_Demo_Widget' );
// Регистрируем две области виджетов.
$sidebars = array ( 'a' => 'top-widget', 'b' => 'bottom-widget' );
foreach ( $sidebars as $sidebar )
{
register_sidebar(
array (
'name' => $sidebar,
'id' => $sidebar,
'before_widget' => '',
'after_widget' => ''
)
);
}
Пока всё просто. Наша тема теперь готова для виджетов, демо-виджет зарегистрирован. Теперь самое интересное.
$active_widgets = get_option( 'sidebars_widgets' );
if ( ! empty ( $active_widgets[ $sidebars['a'] ] )
or ! empty ( $active_widgets[ $sidebars['b'] ] )
)
{ // Окей, веселье закончилось. Здесь уже есть контент.
return;
}
Вы действительно не хотите уничтожить пользовательские настройки. Если в областях виджетов уже есть контент, ваш код не должен его перезаписывать. Поэтому мы останавливаемся в этом случае.
Хорошо, предположим, что области виджетов пусты... нам нужен счётчик:
$counter = 1;
Виджеты нумеруются. Эти номера являются вторыми идентификаторами для WordPress.
Получим массив для изменения:
$active_widgets = get_option( 'sidebars_widgets' );
Нам также нужен счётчик (подробнее об этом позже):
$counter = 1;
Вот как мы используем счётчик, имена областей и аргументы виджета (ну, у нас только один аргумент: text
).
// Добавляем демо-виджет в верхнюю область...
$active_widgets[ $sidebars['a'] ][0] = 't5_demo_widget-' . $counter;
// ... и записываем в него текст:
$demo_widget_content[ $counter ] = array ( 'text' => "Это работает!\n\nУдивительно!" );
$counter++;
Обратите внимание, как создаётся идентификатор виджета: id_base
, минус -
и счётчик. Содержимое виджета хранится в другой переменной $demo_widget_content
. Здесь счётчик является ключом, а аргументы виджета хранятся в массиве.
Мы увеличиваем счётчик на единицу после завершения, чтобы избежать коллизий.
Это было легко. Теперь RSS-виджет. Больше полей, больше веселья!
$active_widgets[ $sidebars['a'] ][] = 'rss-' . $counter;
// Последние 15 вопросов с WordPress Stack Exchange.
$rss_content[ $counter ] = array (
'title' => 'WordPress Stack Exchange',
'url' => 'http://wordpress.stackexchange.com/feeds',
'link' => 'http://wordpress.stackexchange.com/questions',
'items' => 15,
'show_summary' => 0,
'show_author' => 1,
'show_date' => 1,
);
update_option( 'widget_rss', $rss_content );
$counter++;
Здесь кое-что новое: update_option()
сохранит аргументы RSS-виджета в отдельной опции. WordPress автоматически найдёт их позже.
Мы не сохраняли аргументы демо-виджета, потому что добавляем второй экземпляр во вторую область...
// Хорошо, теперь наша вторая область. Сделаем это кратко.
$active_widgets[ $sidebars['b'] ][] = 't5_demo_widget-' . $counter;
#$demo_widget_content = get_option( 'widget_t5_demo_widget', array() );
$demo_widget_content[ $counter ] = array ( 'text' => 'Второй экземпляр нашего удивительного демо-виджета.' );
update_option( 'widget_t5_demo_widget', $demo_widget_content );
... и сохраняем все аргументы для t5_demo_widget
за один раз. Нет необходимости обновлять одну и ту же опцию дважды.
Ну, хватит виджетов на сегодня, давайте сохраним и sidebars_widgets
:
update_option( 'sidebars_widgets', $active_widgets );
Теперь WordPress будет знать, что есть зарегистрированные виджеты и где хранятся аргументы для каждого виджета. Результат var_export()
для sidebar_widgets
будет выглядеть так:
array (
'wp_inactive_widgets' =>
array (
),
'top-widget' =>
array (
0 => 't5_demo_widget-1',
1 => 'rss-2',
),
'bottom-widget' =>
array (
0 => 't5_demo_widget-3',
),
'array_version' => 3,
)
add_action( 'widgets_init', 't5_default_widget_demo' );
function t5_default_widget_demo()
{
// Регистрируем наш виджет.
register_widget( 'T5_Demo_Widget' );
// Регистрируем две области виджетов.
$sidebars = array ( 'a' => 'top-widget', 'b' => 'bottom-widget' );
foreach ( $sidebars as $sidebar )
{
register_sidebar(
array (
'name' => $sidebar,
'id' => $sidebar,
'before_widget' => '',
'after_widget' => ''
)
);
}
// Хорошо, теперь самое интересное.
// Мы не хотим отменять пользовательские изменения, поэтому сначала проверим их.
$active_widgets = get_option( 'sidebars_widgets' );
if ( ! empty ( $active_widgets[ $sidebars['a'] ] )
or ! empty ( $active_widgets[ $sidebars['b'] ] )
)
{ // Окей, веселье закончилось. Здесь уже есть контент.
return;
}
// Области виджетов пусты, давайте добавим в них что-нибудь.
// Например, RSS-виджет и два экземпляра нашего демо-виджета?
// Виджеты нумеруются. Нам нужен счётчик:
$counter = 1;
// Добавляем демо-виджет в верхнюю область...
$active_widgets[ $sidebars['a'] ][0] = 't5_demo_widget-' . $counter;
// ... и записываем в него текст:
$demo_widget_content[ $counter ] = array ( 'text' => "Это работает!\n\nУдивительно!" );
#update_option( 'widget_t5_demo_widget', $demo_widget_content );
$counter++;
// Это было легко. Теперь RSS-виджет. Больше полей, больше веселья!
$active_widgets[ $sidebars['a'] ][] = 'rss-' . $counter;
// Последние 15 вопросов с WordPress Stack Exchange.
$rss_content[ $counter ] = array (
'title' => 'WordPress Stack Exchange',
'url' => 'http://wordpress.stackexchange.com/feeds',
'link' => 'http://wordpress.stackexchange.com/questions',
'items' => 15,
'show_summary' => 0,
'show_author' => 1,
'show_date' => 1,
);
update_option( 'widget_rss', $rss_content );
$counter++;
// Хорошо, теперь наша вторая область. Сделаем это кратко.
$active_widgets[ $sidebars['b'] ][] = 't5_demo_widget-' . $counter;
#$demo_widget_content = get_option( 'widget_t5_demo_widget', array() );
$demo_widget_content[ $counter ] = array ( 'text' => 'Второй экземпляр нашего удивительного демо-виджета.' );
update_option( 'widget_t5_demo_widget', $demo_widget_content );
// Теперь сохраняем массив $active_widgets.
update_option( 'sidebars_widgets', $active_widgets );
}
Если вы перейдёте в wp-admin/widgets.php
, то увидите три предустановленных виджета:
И это всё. Используйте...
dynamic_sidebar( 'top-widget' );
dynamic_sidebar( 'bottom-widget' );
... чтобы вывести виджеты на экран.
Есть небольшой нюанс: для первоначальной регистрации нужно загрузить фронтенд дважды. Если кто-то может помочь с этим, я буду очень благодарен.

Это действительно интересно... но не будет ли этот код добавлять "новый" виджет при каждой загрузке страницы? Еще один интересный момент — как можно управлять этими виджетами, включая их содержимое, из плагина, а не из темы (которая загружается раньше?).

@krembo99 Виджеты не добавляются, если сайдбары не пусты. Код работает в плагине точно так же.

На что здесь ссылается widget_t5_demo_widget
в строке: update_option( 'widget_t5_demo_widget', $demo_widget_content );
?

Я нашёл этот ответ информативным для понимания того, как данные хранятся/используются. Затем мне понравился этот другой ответ с практическим примером кода: https://wordpress.stackexchange.com/a/138248/27896

Спасибо, что поделились своим решением. Я использовал описанный в этом вопросе подход для создания кода, который позволяет очень легко инициализировать боковые панели. Он очень гибкий — вы можете создавать сколько угодно виджетов, вообще не изменяя код. Просто используйте фильтры и передавайте аргументы в виде массива. Вот код с комментариями:
function initialize_sidebars(){
$sidebars = array();
// Передаем боковые панели, которые хотим инициализировать, через фильтр
$sidebars = apply_filters( 'alter_initialization_sidebars', $sidebars );
$active_widgets = get_option('sidebars_widgets');
$args = array(
'sidebars' => $sidebars,
'active_widgets' => $active_widgets,
'update_widget_content' => array(),
);
foreach ( $sidebars as $current_sidebar_short_name => $current_sidebar_id ) {
$args['current_sidebar_short_name'] = $current_sidebar_short_name;
// Передаем аргументы по ссылке, чтобы можно было изменять их содержимое
do_action( 'your_plugin_sidebar_init', array( &$args ) );
}
// Обновляем боковые панели только если они еще не инициализированы
// и у нас есть данные для их инициализации
if ( ! empty( $args['update_widget_content'] ) ) {
foreach ( $args['update_widget_content'] as $widget => $widget_occurence ) {
// Массив update_widget_content хранит все экземпляры каждого виджета
update_option( 'widget_' . $widget, $args['update_widget_content'][ $widget ] );
}
// После обновления всех виджетов обновляем массив active_widgets
update_option( 'sidebars_widgets', $args['active_widgets'] );
}
}
Это вспомогательная функция, которая проверяет, есть ли уже контент в боковой панели:
function check_sidebar_content( $active_widgets, $sidebars, $sidebar_name ) {
$sidebar_contents = $active_widgets[ $sidebars[ $sidebar_name ] ];
if ( ! empty( $sidebar_contents ) ) {
return $sidebar_contents;
}
return false;
}
Теперь создадим функцию, которая будет подключена к действию 'sidebar_init'.
add_action( 'your_plugin_sidebar_init', 'add_widgets_to_sidebar' );
function add_widgets_to_sidebar( $args ) {
extract( $args[0] );
// Проверяем, есть ли уже контент в текущей боковой панели, и если да, выходим
$sidebar_element = check_sidebar_content( $active_widgets, $sidebars, $current_sidebar_short_name );
if ( $sidebar_element !== false ) {
return;
}
do_action( 'your_plugin_widget_init', array( &$args ) );
}
Теперь инициализация виджетов:
add_action( 'your_plugin_widget_init', 'your_plugin_initialize_widgets' );
function your_plugin_initialize_widgets( $args ) {
extract( $args[0][0] );
$widgets = array();
// Здесь инициализируются виджеты, определенные ранее в функциях фильтров,
// но только те, которые соответствуют текущей боковой панели
$widgets = apply_filters( 'alter_initialization_widgets_' . $current_sidebar_short_name, $widgets );
if ( ! empty( $widgets ) ) {
do_action( 'create_widgets_for_sidebar', array( &$args ), $widgets );
}
}
Последнее действие — создание виджетов в каждой боковой панели:
add_action( 'create_widgets_for_sidebar', 'your_plugin_create_widgets', 10, 2 );
function your_plugin_create_widgets( $args, $widgets ) {
extract( $args[0][0][0] );
foreach ( $widgets as $widget => $widget_content ) {
// Счетчик увеличивается для каждого виджета. Например, если у вас три виджета,
// два из них — виджеты архивов, а один — пользовательский виджет, то правильные
// счетчики для них будут archive-1, archive-2 и custom-1.
// Таким образом, счетчик виджетов не глобальный, а учитывает экземпляры
// (widget_occurrence) каждого виджета.
$counter = count_widget_occurence( $widget, $args[0][0][0]['update_widget_content'] );
// Добавляем каждый экземпляр в активные виджеты...
$args[0][0][0]['active_widgets'][ $sidebars[ $current_sidebar_short_name ] ][] = $widget . '-' . $counter;
// ...и сохраняем контент в другом ассоциативном массиве.
$args[0][0][0]['update_widget_content'][ $widget ][ $counter ] = $widget_content;
}
}
Эта функция используется для отслеживания количества экземпляров конкретного виджета, которые уже были определены:
function count_widget_occurence( $widget, $update_widget_content ) {
$widget_occurrence = 0;
// Смотрим на массив update_widget_content, который хранит каждый
// экземпляр текущего виджета с текущим счетчиком в ассоциативном
// массиве. Ключ этого массива — имя текущего виджета.
// Например, три виджета архивов будут выглядеть так:
// 'update_widget_content'['archives'] => [1][2][3]
if ( array_key_exists( $widget, $update_widget_content ) ) {
$widget_counters = array_keys( $update_widget_content[ $widget ] );
$widget_occurrence = end( $widget_counters );
}
$widget_occurrence++;
return $widget_occurrence;
}
Последнее, что нужно сделать, — это назначить значения. Используйте эти функции фильтров:
add_filter( 'alter_initialization_sidebars', 'current_initialization_sidebars' ) ;
// Используйте этот фильтр, чтобы указать, какие боковые панели вы хотите инициализировать
function current_initialization_sidebars( $sidebars ) {
// Боковые панели назначаются таким образом.
// Ключ массива очень важен, так как он используется как суффикс в функции инициализации
// для каждой боковой панели. Значение — это то, что используется в HTML-атрибутах.
$sidebars['info'] = 'info-sidebar';
return $sidebars;
}
И:
add_filter( 'alter_initialization_widgets_info', 'current_info_widgets' );
// Добавьте фильтр для каждой боковой панели. Имя хука формируется из
// ключей массива, переданных в фильтре alter_initialization_sidebars.
// Каждый фильтр имеет имя 'alter_initialization_widgets_' с добавленным
// ключом массива.
function current_info_widgets( $widgets ) {
// Эта функция фильтра используется для добавления виджетов в боковую панель info.
// Добавьте каждый виджет, который хотите назначить этой панели, в массив.
return $widgets = array(
// Используйте имя виджета, указанное в вызове конструктора WP_Widget,
// в качестве ключа массива.
// Виджет архивов — это виджет, который поставляется с WordPress по умолчанию.
// Аргументы, используемые этим виджетом, как и всеми другими виджетами по умолчанию,
// можно найти в wp-includes/default-widgets.php.
'archives' => array(
// Передайте параметры в виде массива
'title' => 'Старые записи',
'dropdown' => 'on',
// Значение 'on' выбрано произвольно — виджет фактически проверяет только
// непустое значение для этих опций
'count' => 'on',
),
);
}
В идеале вы должны вызывать initialize_sidebars в функции настройки, которая вызывается при активации темы или плагина, например так: Активация темы:
add_action( 'after_switch_theme', 'my_activation_function' );
function my_activation_function() {
initialize_sidebars();
}
Активация плагина:
register_activation_hook( __FILE__, 'my_activation_function' );
function my_activation_function() {
initialize_sidebars();
}
Подведем итог по использованию этого набора функций:
Создайте функцию, которая инициализирует боковые панели, подключенную к фильтру 'alter_initialization_sidebars'.
Создайте функцию для каждой боковой панели, которую вы только что добавили, подключенную к фильтру 'alter_initialization_widgets_$sidebarname'. Замените $sidebarname на имя каждой боковой панели, созданной в шаге 1.
Вы также можете просто скопировать этот код без комментариев в файл functions.php и сразу начать создавать свои функции фильтров: Код на pastie (без функций фильтров инициализации)

Прежде всего, спасибо @toscho за подробный ответ.
Это простой пример для тех, кто ищет простое решение и стандартные настройки виджетов:
$active_sidebars = get_option( 'sidebars_widgets' ); // получаем все сайдбары и виджеты
$widget_options = get_option( 'widget_name-1' );
$widget_options[1] = array( 'option1' => 'value', 'option2' => 'value2' );
if(isset($active_sidebars['sidebar-id']) && empty($active_sidebars['sidebar-id'])) { // проверяем, существует ли сайдбар и пуст ли он
$active_sidebars['sidebar-id'] = array('widget_name-1'); // добавляем виджет в сайдбар
update_option('widget_name-1', $widget_options); // обновляем стандартные настройки виджета
update_option('sidebars_widgets', $active_sidebars); // обновляем сайдбары
}
Примечание 1: Вы можете получить sidebar-id
, перейдя в меню виджетов и проверив нужный сайдбар. Первый дочерний элемент <div>
у <div id="widgets-holder-wrap">
содержит sidebar-id
.
Примечание 2: Вы можете получить widget_name
, перейдя в меню виджетов и проверив нужный виджет. Вы увидите что-то вроде <div id="widget-6_widget_name-__i__" class="widget ui-draggable">
.
Надеюсь, это поможет.

Вот как это делается:
(ВНИМАНИЕ, это может УДАЛИТЬ все предыдущие виджеты, если вы не вернете оригинальные виджеты обратно в массив widgets
.)
$widgets = array(
'middle-sidebar' => array(
'widget_name'
),
'right-sidebar' => array(
'widget2_name-1'
)
);
update_option('sidebars_widgets', $widgets);
Суффикс -number можно использовать, если позже вы захотите добавить настройки для виджета с помощью такого кода:
update_option('widget_widget_name', array(
1 => array(
'title' => 'Заголовок',
'number' => 4
),
'_multiwidget' => 1
));

НЕ ИСПОЛЬЗУЙТЕ ЭТО, Я НЕ МОГУ ОЦЕНИТЬ ЭТО ОТРИЦАТЕЛЬНО. ВСЕ МОИ ВИДЖЕТЫ ИСЧЕЗЛИ ПОСЛЕ ИСПОЛЬЗОВАНИЯ ЭТОГО КОДА.
