Agregar widgets a las barras laterales programáticamente

22 ago 2011, 14:45:35
Vistas: 39.3K
Votos: 71

Me gustaría agregar widgets programáticamente a mis dos barras laterales. No he podido encontrar ninguna forma oficial de hacerlo.

Comencé a buscar en la base de datos. He encontrado que es la opción 'sidebars_widgets' la que coloca los widgets en las barras laterales. Al revisar las opciones, los nombres de los widgets tienen un número agregado al final, como: widget_name-6. ¿De dónde viene ese número?

¿Alguna idea de cómo resolver esto?

5
Comentarios

Deberías agregar tu respuesta ahí abajo para responder tu propia pregunta :)

helenhousandi helenhousandi
22 ago 2011 18:28:54

Para un excelente resumen sobre widgets de barra lateral, revisa este artículo: http://justintadlock.com/archives/2010/11/08/sidebars-in-wordpress.

Joshua Joshua
22 ago 2011 18:58:07

Monitorea el parámetro de acción de la llamada ajax que se realiza cuando se agrega un widget, y luego encuentra el código relacionado con ese hook de acción ajax y mira cómo se hace en el núcleo. ¡Sencillo! ;)

Ashfame Ashfame
10 abr 2012 00:50:44

Por favor, vuelve a publicar tu solución como una respuesta y acéptala como "la" respuesta a tu problema.

EAMann EAMann
12 abr 2012 00:52:54
Todas las respuestas a la pregunta 4
5
102

Cuando comencé esta respuesta debería ser solo una pequeña nota. Bueno, fallé. ¡Lo siento! Quédate conmigo, hay un regalo escondido en lo profundo...

Cómo se almacenan los widgets de WordPress

La lista de widgets se almacena en una opción llamada 'sidebars_widgets'. Un var_export() puede dar algo como lo siguiente:

array (
  'wp_inactive_widgets' => 
  array (
  ),
  'top-widget' => 
  array (
  ),
  'bottom-widget' => 
  array (
  ),
  'array_version' => 3,
)

Ignora 'wp_inactive_widgets' y 'array_version'. No tenemos que preocuparnos por esos.
Las otras claves son identificadores para las barras laterales registradas. En este caso, las barras laterales pueden haberse registrado con este código:

// Registrar dos barras laterales.
$sidebars = array ( 'a' => 'top-widget', 'b' => 'bottom-widget' );
foreach ( $sidebars as $sidebar )
{
    register_sidebar(
        array (
            'name'          => $sidebar,
            'id'            => $sidebar,
            'before_widget' => '',
            'after_widget'  => ''
        )
    );
}

Por defecto, las barras laterales están vacías después del registro. Por supuesto.

Para cada clase de widget registrada se crea una opción separada, que contiene todas las opciones necesarias. La opción tiene el prefijo de la cadena widget_. Para obtener las opciones de todos los widgets RSS activos tenemos que mirar en...

get_option( 'widget_rss' );

Posible salida:

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,
  ),
)

Observa el número 2. Los argumentos para múltiples instancias se almacenan todos en esta opción ordenados por números.

Para ver qué clases de widget ya son conocidas por WordPress ve a wp-admin/options.php y desplázate hacia abajo hasta que veas algo como esto:

captura de pantalla de opciones de widget serializadas

Sí, datos serializados. No, no puedes leerlos aquí. No te preocupes, no tienes que hacerlo.

Un widget de demostración

Para ilustrar mejor el funcionamiento interno he escrito un widget de demostración muy simple:

/**
 * Widget super simple.
 */
class T5_Demo_Widget extends WP_Widget
{
    public function __construct()
    {                      // id_base        ,  nombre visible
        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
        );
    }
}

Observa el constructor: 't5_demo_widget' es el $id_base, el identificador para este widget. Como puedes ver en la captura de pantalla, sus argumentos se almacenan en la opción widget_t5_demo_widget. Todos tus widgets personalizados serán tratados así. No tienes que adivinar el nombre. Y dado que has escrito tus widgets (probablemente) conoces todos los argumentos de los parámetros $instance de tu clase.

Conceptos básicos del tema

Primero tienes que registrar algunas barras laterales y el widget personalizado. La acción adecuada para esto es fácil de recordar: 'widgets_init'. Pon todo en un contenedor: una clase o una función. Por simplicidad usaré una función llamada t5_default_widget_demo().

Todo el siguiente código va en el functions.php. La clase T5_Demo_Widget debería estar cargada ya. Simplemente la pongo en el mismo archivo...

add_action( 'widgets_init', 't5_default_widget_demo' );

function t5_default_widget_demo()
{
    // Registrar nuestro propio widget.
    register_widget( 'T5_Demo_Widget' );

    // Registrar dos barras laterales.
    $sidebars = array ( 'a' => 'top-widget', 'b' => 'bottom-widget' );
    foreach ( $sidebars as $sidebar )
    {
        register_sidebar(
            array (
                'name'          => $sidebar,
                'id'            => $sidebar,
                'before_widget' => '',
                'after_widget'  => ''
            )
        );
    }

Hasta aquí, todo simple. Nuestro tema ahora está listo para widgets, el widget de demostración es conocido. Ahora la diversión.

$active_widgets = get_option( 'sidebars_widgets' );

if ( ! empty ( $active_widgets[ $sidebars['a'] ] )
    or ! empty ( $active_widgets[ $sidebars['b'] ] )
)
{   // Vale, no más diversión. Ya hay algún contenido.
    return;
}

Realmente no quieres destruir la configuración del usuario. Si ya hay algún contenido en las barras laterales, tu código no debería sobrescribirlo. Por eso nos detenemos en este caso.

Vale, asumiendo que las barras laterales están vacías... necesitamos un contador:

$counter = 1;

Los widgets están numerados. Estos números son segundos identificadores para WordPress.

Obtenemos el array para cambiarlo:

$active_widgets = get_option( 'sidebars_widgets' );

También necesitamos un contador (más sobre esto más adelante):

$counter = 1;

Y así es como usamos el contador, los nombres de las barras laterales y los argumentos del widget (bueno, solo tenemos un argumento: text).

// Añadir un widget 'demo' a la barra lateral superior...
$active_widgets[ $sidebars['a'] ][0] = 't5_demo_widget-' . $counter;
// ... y escribir algo de texto en él:
$demo_widget_content[ $counter ] = array ( 'text' => "¡Esto funciona!\n\n¡Increíble!" );

$counter++;

Observa cómo se crea el identificador del widget: el id_base, un guión - y el contador. El contenido del widget se almacena en otra variable $demo_widget_content. Aquí el contador es la clave y los argumentos del widget se almacenan en un array.

Incrementamos el contador en uno cuando terminamos para evitar colisiones.

Eso fue fácil. Ahora un widget RSS. ¡Más campos, más diversión!

$active_widgets[ $sidebars['a'] ][] = 'rss-' . $counter;
// Las últimas 15 preguntas de 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++;

Aquí hay algo nuevo: update_option() esto almacenará los argumentos del widget RSS en una opción separada. WordPress encontrará estos automáticamente más tarde.
No guardamos los argumentos del widget de demostración porque añadimos una segunda instancia a nuestra segunda barra lateral ahora...

// Vale, ahora a nuestra segunda barra lateral. Lo hacemos breve.
$active_widgets[ $sidebars['b'] ][] = 't5_demo_widget-' . $counter;
#$demo_widget_content = get_option( 'widget_t5_demo_widget', array() );
$demo_widget_content[ $counter ] = array ( 'text' => 'La segunda instancia de nuestro increíble widget de demostración.' );
update_option( 'widget_t5_demo_widget', $demo_widget_content );

... y guardamos todos los argumentos para el t5_demo_widget de una vez. No hay necesidad de actualizar la misma opción dos veces.

Bueno, suficientes widgets por hoy, guardemos también sidebars_widgets:

update_option( 'sidebars_widgets', $active_widgets );

Ahora WordPress sabrá que hay algunos widgets registrados y dónde se almacenan los argumentos para cada widget. Un var_export() en los sidebar_widgets se verá así:

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,
)

El código completo de nuevo:

add_action( 'widgets_init', 't5_default_widget_demo' );

function t5_default_widget_demo()
{
    // Registrar nuestro propio widget.
    register_widget( 'T5_Demo_Widget' );

    // Registrar dos barras laterales.
    $sidebars = array ( 'a' => 'top-widget', 'b' => 'bottom-widget' );
    foreach ( $sidebars as $sidebar )
    {
        register_sidebar(
            array (
                'name'          => $sidebar,
                'id'            => $sidebar,
                'before_widget' => '',
                'after_widget'  => ''
            )
        );
    }

    // Vale, ahora la parte divertida.

    // No queremos deshacer los cambios del usuario, así que buscamos cambios primero.
    $active_widgets = get_option( 'sidebars_widgets' );

    if ( ! empty ( $active_widgets[ $sidebars['a'] ] )
        or ! empty ( $active_widgets[ $sidebars['b'] ] )
    )
    {   // Vale, no más diversión. Ya hay algún contenido.
        return;
    }

    // Las barras laterales están vacías, pongamos algo en ellas.
    // ¿Qué tal un widget RSS y dos instancias de nuestro widget de demostración?

    // Nota que los widgets están numerados. Necesitamos un contador:
    $counter = 1;

    // Añadir un widget 'demo' a la barra lateral superior...
    $active_widgets[ $sidebars['a'] ][0] = 't5_demo_widget-' . $counter;
    // ... y escribir algo de texto en él:
    $demo_widget_content[ $counter ] = array ( 'text' => "¡Esto funciona!\n\n¡Increíble!" );
    #update_option( 'widget_t5_demo_widget', $demo_widget_content );

    $counter++;

    // Eso fue fácil. Ahora un widget RSS. ¡Más campos, más diversión!
    $active_widgets[ $sidebars['a'] ][] = 'rss-' . $counter;
    // Las últimas 15 preguntas de 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++;

    // Vale, ahora a nuestra segunda barra lateral. Lo hacemos breve.
    $active_widgets[ $sidebars['b'] ][] = 't5_demo_widget-' . $counter;
    #$demo_widget_content = get_option( 'widget_t5_demo_widget', array() );
    $demo_widget_content[ $counter ] = array ( 'text' => 'La segunda instancia de nuestro increíble widget de demostración.' );
    update_option( 'widget_t5_demo_widget', $demo_widget_content );

    // Ahora guardamos el array $active_widgets.
    update_option( 'sidebars_widgets', $active_widgets );
}

Si vas a wp-admin/widgets.php ahora verás tres widgets preestablecidos:

captura de pantalla de widgets activos

Y eso es todo. Usa...

dynamic_sidebar( 'top-widget' );
dynamic_sidebar( 'bottom-widget' );

... para mostrar los widgets.

Hay un pequeño problema: Tienes que cargar el front-end dos veces para el registro inicial. Si alguien puede ayudar aquí estaré muy agradecido.

4 may 2012 05:23:58
Comentarios

Esto es realmente interesante... pero ¿no agregaría este código un widget "nuevo" en cada carga de página? También, otro tema interesante es cómo se pueden controlar esos widgets, incluyendo su contenido desde un plugin en lugar del tema (¿se carga antes?).

krembo99 krembo99
3 ago 2013 11:36:39

@krembo99 Los widgets no se añaden cuando las barras laterales no están vacías. El código funciona exactamente igual en un plugin.

fuxia fuxia
3 ago 2013 14:46:10

¿A qué se refiere widget_t5_demo_widget aquí: update_option( 'widget_t5_demo_widget', $demo_widget_content );?

Snowcrash Snowcrash
16 oct 2015 18:29:01

@SnowCrash Ese es solo el nombre de una opción, sin referencia a nada más.

fuxia fuxia
16 oct 2015 18:37:35

Encontré esta respuesta informativa para entender cómo se almacenan/usan los datos. Luego me gustó esta otra respuesta por el ejemplo de código práctico: https://wordpress.stackexchange.com/a/138248/27896

Tyler Collier Tyler Collier
29 sept 2020 00:13:23
0

Gracias por compartir tu solución. He utilizado lo descrito en esta pregunta para crear un fragmento de código que puede usarse para inicializar barras laterales muy fácilmente. Es muy flexible, puedes crear tantos widgets como quieras sin tener que modificar el código en absoluto. Solo haz uso de los filtros y pasa argumentos en un array. Aquí está el código comentado:

function initialize_sidebars(){

  $sidebars = array();
  // Proporciona las barras laterales que deseas inicializar en un filtro
  $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;
    // pasamos nuestros argumentos como referencia, para poder modificar su contenido
    do_action( 'your_plugin_sidebar_init', array( &$args ) );

  }
  // solo necesitamos actualizar las barras laterales si no están inicializadas todavía
  // y también tenemos datos para inicializarlas
  if ( ! empty( $args['update_widget_content'] ) {

    foreach ( $args['update_widget_content'] as $widget => $widget_occurence ) {

      // el array update_widget_content almacena todas las instancias de cada widget
      update_option( 'widget_' . $widget, $args['update_widget_content'][ $widget ] );

    }
    // después de actualizar todos los widgets, actualizamos el array active_widgets
    update_option( 'sidebars_widgets', $args['active_widgets'] );

  }

}

Esta es una función auxiliar que verifica si la barra lateral ya tiene contenido:

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;

}

Ahora necesitamos crear una función que esté enganchada a la acción 'sidebar_init'.

add_action( 'your_plugin_sidebar_init', 'add_widgets_to_sidebar' );

function add_widgets_to_sidebar( $args ) {

  extract( $args[0] );

  // Verificamos si la barra lateral actual ya tiene contenido y, si es así, salimos
  $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 ) );

}

Y ahora la inicialización de widgets:

add_action( 'your_plugin_widget_init', 'your_plugin_initialize_widgets' );

function your_plugin_initialize_widgets( $args ) {

  extract( $args[0][0] );

  $widgets = array();

  // Aquí se inicializan los widgets previamente definidos en funciones de filtro,
  // pero solo aquellos correspondientes a la barra lateral actual
  $widgets = apply_filters( 'alter_initialization_widgets_' . $current_sidebar_short_name, $widgets );

  if ( ! empty( $widgets ) ) {

    do_action( 'create_widgets_for_sidebar', array( &$args ), $widgets );

  }

}

La última acción es crear los widgets en cada barra lateral:

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 ) {

    // El contador se incrementa por widget. Por ejemplo, si tuvieras tres widgets,
    // dos de ellos siendo el widget de archivos y uno siendo un widget personalizado, entonces
    // el contador correcto añadido a cada uno sería archive-1, archive-2 y custom-1.
    // Así que el contador de widgets no es global, sino que cuenta las instancias (la
    // ocurrencia del widget como la he llamado) de cada widget.
    $counter = count_widget_occurence( $widget, $args[0][0][0]['update_widget_content'] );

    // Añadimos cada instancia a los widgets activos...
    $args[0][0][0]['active_widgets'][ $sidebars[ $current_sidebar_short_name ] ][] = $widget . '-' . $counter;

    // ...y también guardamos el contenido en otro array asociativo.
    $args[0][0][0]['update_widget_content'][ $widget ][ $counter ] = $widget_content;

  }

}

Esta función se utiliza para llevar la cuenta de cuántas instancias de un widget específico ya se han definido:

function count_widget_occurence( $widget, $update_widget_content ) {

  $widget_occurrence = 0;

  // Miramos el array update_widget_content que almacena cada
  // instancia del widget actual con el contador actual en un
  // array asociativo. La clave de este array es el nombre del
  // widget actual.
      // Tener tres widgets de archivos, por ejemplo, se vería así:
      // '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;

}

Lo último que necesitamos hacer es asignar valores. Utiliza estas funciones de filtro:

add_filter( 'alter_initialization_sidebars', 'current_initialization_sidebars' ) ;
// Usa este filtro para especificar qué barras laterales quieres inicializar
function current_initialization_sidebars( $sidebars ) {

  // Las barras laterales se asignan de esta manera.
  // La clave del array es muy importante porque se usa como sufijo en la función de inicialización
  // para cada barra lateral. El valor es lo que se usa en los atributos HTML.
  $sidebars['info'] = 'info-sidebar';

  return $sidebars;

}

Y:

add_filter( 'alter_initialization_widgets_info', 'current_info_widgets' );
// Añade un filtro para cada barra lateral que tengas. El nombre del gancho se deriva de
// las claves del array pasado en el filtro alter_initialization_sidebars. 
// Cada filtro tiene un nombre de 'alter_initialization_widgets_' con la clave 
// del array añadida al final.

function current_info_widgets( $widgets ) {
  // Esta función de filtro se usa para añadir widgets a la barra lateral info. Añade cada widget
  // que quieras asignar a esta barra lateral en un array.

  return $widgets = array(
    // Usa el nombre del widget como se especifica en la llamada al constructor WP_Widget
    // como clave del array.

    // El widget de archivos es un widget que viene por defecto con WordPress.
    // Los argumentos usados por este widget, como todos los widgets por defecto, se encuentran
    // en wp-includes/default-widgets.php. 

    'archives' => array(
      // Pasa las opciones del array como un array
      'title' => 'Contenido Antiguo',
      'dropdown' => 'on',
      // El valor 'on' se elige arbitrariamente, el widget solo verifica que
      // haya un valor no vacío en ambas opciones
      'count' => 'on',
    ),
 );

}

Idealmente, llamarías a initialize_sidebars en una función de configuración que se ejecuta al activar el plugin o el tema así: Activación del tema:

add_action( 'after_switch_theme', 'my_activation_function' );
function my_activation_function() {
  initialize_sidebars();
}

Activación del plugin:

register_activation_hook( __FILE__, 'my_activation_function' );
function my_activation_function() {
  initialize_sidebars();
}

Para resumir el uso de este conjunto de funciones:

  1. Crea una función que inicialice las barras laterales que esté enganchada al filtro 'alter_initialization_sidebars'.

  2. Crea una función para cada barra lateral que acabas de añadir, enganchada al filtro 'alter_initialization_widgets_$nombredelabarra'. Reemplaza $nombredelabarra con el nombre de cada barra lateral que creaste en el paso 1.

También puedes copiar este código sin comentarios en tu archivo de funciones y empezar a crear tus funciones de filtro de inmediato: Código en pastie (sin funciones de filtro de inicialización)

12 oct 2014 15:25:38
0

En primer lugar, gracias a @toscho por la respuesta detallada.

Este es un ejemplo simple para aquellos que buscan una solución sencilla y opciones predeterminadas de widgets:

$active_sidebars = get_option( 'sidebars_widgets' ); //obtener todas las barras laterales y 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'])) { //verificar si la barra lateral existe y está vacía

    $active_sidebars['sidebar-id'] = array('widget_name-1'); //agregar un widget a la barra lateral
    update_option('widget_name-1', $widget_options); //actualizar opciones predeterminadas del widget
    update_option('sidebars_widgets', $active_sidebars); //actualizar barras laterales
}

Nota 1: Puedes obtener el sidebar-id yendo al menú de widgets e inspeccionando la barra lateral deseada. El primer hijo <div> de <div id="widgets-holder-wrap"> tiene el sidebar-id.

Nota 2: Puedes obtener el widget_name yendo al menú de widgets e inspeccionando el widget deseado. Verás algo como <div id="widget-6_widget_name-__i__" class="widget ui-draggable">.

Espero que ayude.

19 ago 2014 11:59:19
2

Así es como se hace:

(ADVERTENCIA, esto podría ELIMINAR todos los widgets anteriores si no devuelves los widgets originales al array widgets.)

    $widgets = array(
    'middle-sidebar' => array(
        'widget_name'
    ),
    'right-sidebar' => array(
        'widget2_name-1'
    )
);
update_option('sidebars_widgets', $widgets);

El -number se puede utilizar si posteriormente deseas agregar opciones al widget con algo como esto:

    update_option('widget_widget_name', array(
    1 => array(
        'title' => 'El título',
        'number' => 4
    ),
    '_multiwidget' => 1
));
4 may 2012 05:13:41
Comentarios

NO SIGAS ESTO, NO PUEDO CALIFICAR ESTO NEGATIVAMENTE. TODOS MIS WIDGETS DESAPARECIERON DESPUÉS DE USAR ESTE CÓDIGO.

EresDev EresDev
12 jul 2014 02:34:58

Necesitas obtener primero el array de widgets existente, de lo contrario los borrarás todos como se menciona en el comentario anterior. $widgets = get_option( 'sidebars_widgets' );

cowgill cowgill
11 nov 2016 12:03:20