Программное добавление виджетов в сайдбары

22 авг. 2011 г., 14:45:35
Просмотры: 39.3K
Голосов: 71

Я хотел бы программно добавить виджеты в мои два сайдбара. Я не смог найти официальный способ сделать это?

Я начал искать в базе данных. Я обнаружил, что за размещение виджетов в сайдбарах отвечает опция 'sidebars_widgets'. При просмотре опций к названиям виджетов добавляется число, например: widget_name-6. Откуда берется это число?

Есть какие-нибудь идеи, как решить эту задачу?

5
Комментарии

Вы должны добавить свой ответ ниже, чтобы ответить на свой собственный вопрос :)

helenhousandi helenhousandi
22 авг. 2011 г. 18:28:54

Для отличного обзора виджетов боковой панели ознакомьтесь с этой статьей: http://justintadlock.com/archives/2010/11/08/sidebars-in-wordpress.

Joshua Joshua
22 авг. 2011 г. 18:58:07

Следите за параметром action AJAX-запроса, который выполняется при добавлении виджета, затем найдите код, связанный с этим хуком AJAX, и посмотрите, как это реализовано в ядре. Просто! ;)

Ashfame Ashfame
10 апр. 2012 г. 00:50:44

Пожалуйста, повторно опубликуйте ваше решение в качестве ответа и примите его как "правильный" ответ на вашу проблему.

EAMann EAMann
12 апр. 2012 г. 00:52:54
Все ответы на вопрос 4
5
102

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

Как виджеты 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' );

... чтобы вывести виджеты на экран.

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

4 мая 2012 г. 05:23:58
Комментарии

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

krembo99 krembo99
3 авг. 2013 г. 11:36:39

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

fuxia fuxia
3 авг. 2013 г. 14:46:10

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

Snowcrash Snowcrash
16 окт. 2015 г. 18:29:01

@SnowCrash Это просто название опции, без отсылки к чему-либо ещё.

fuxia fuxia
16 окт. 2015 г. 18:37:35

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

Tyler Collier Tyler Collier
29 сент. 2020 г. 00:13:23
0

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

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();
}

Подведем итог по использованию этого набора функций:

  1. Создайте функцию, которая инициализирует боковые панели, подключенную к фильтру 'alter_initialization_sidebars'.

  2. Создайте функцию для каждой боковой панели, которую вы только что добавили, подключенную к фильтру 'alter_initialization_widgets_$sidebarname'. Замените $sidebarname на имя каждой боковой панели, созданной в шаге 1.

Вы также можете просто скопировать этот код без комментариев в файл functions.php и сразу начать создавать свои функции фильтров: Код на pastie (без функций фильтров инициализации)

12 окт. 2014 г. 15:25:38
0

Прежде всего, спасибо @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">.

Надеюсь, это поможет.

19 авг. 2014 г. 11:59:19
2

Вот как это делается:

(ВНИМАНИЕ, это может УДАЛИТЬ все предыдущие виджеты, если вы не вернете оригинальные виджеты обратно в массив 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
));
4 мая 2012 г. 05:13:41
Комментарии

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

EresDev EresDev
12 июл. 2014 г. 02:34:58

Сначала вам нужно получить существующий массив виджетов, иначе вы удалите их все, как указано в комментарии выше. $widgets = get_option( 'sidebars_widgets' );

cowgill cowgill
11 нояб. 2016 г. 12:03:20