Como diseñador y desarrollador web que utiliza WordPress como framework, muchas veces me he visto en la obligación de tunear WordPress para conseguir otro tipo de tareas que en una instalación por defecto sería imposible.

En la anterior entrada os traía un tutorial sobre Custom Post Types donde os explicaba la forma correcta de utilizar las entradas personalizadas para sacar más provecho a WordPress.

En esta ocasión os traigo una pequeña ampliación (aunque nada tiene que ver con los custom post types) con lo que podremos crear fichas de productos o darle un vitaminado general a las entradas (sean personalizadas o no) de nuestro WordPress.

¿Qué es un metabox en WordPress?

Para quien no lo sepa, un metabox nos permite añadir campos extra a la página de edición de las entradas (como dije antes, sean del tipo que sean). Con esto podemos guardar más información sobre un mismo tipo de contenido. El ejemplo que nos servirá, en esta entrada, será el mismo que utilizamos en los custom post types de la entrada anterior.

Poniéndonos en situación, habíamos creado un custom post type “Libros” con el que añadir fichas de libros a nuestro WordPress; en el siempre hipotético caso de que nuestra web fuese a tratar de libros. Esto nos permitiría tener el blog original de WordPress intacto y aparte las fichas de los libros.

¿Cómo añadimos Metaboxes?

Te podría dar la respuesta sencilla de “con un plugin“, pero si lees de forma regular mi blog sabrás que odio la dependencia de plugins o, al menos, intento evitarla al máximo; sobretodo en proyectos más grandes.

custom post type

En esta imagen vemos cómo se ve la página de edición de nuestro custom post type de libros. Vamos a añadirle un metabox con algunos datos más.

Para añadir un metabox, basta con agregar este código al functions.php de nuestro tema:

<?php add_meta_box( $id, $title, $callback, $page, $context, $priority, $callback_args ); ?>

Donde entendemos que:

Tras la breve explicación, nuestra llamada en el functions.php quedará así:

add_meta_box( 'my-meta-box-id', 'Datos adicionales del libro', 'cd_meta_box_cb', 'libro', 'normal', 'high' );

Para finalizar, crearemos una función que cree nuestro metabox (Esto ya lo puedes pegar en el functions.php):

add_action( 'add_meta_boxes', 'dariobf_metabox' );
function dariobf_metabox()
{
	add_meta_box( 'my-meta-box-id', 'Datos adicionales del libro', 'dariobf_metabox', 'libro', 'normal', 'high' );
}

Ahora veremos una página de edición más similar a esto:
custom metabox

Sigue faltando algo, ¿verdad?

Dándole forma al metabox

Esto del metabox está muy bien, pero no hemos añadido nada útil hasta el momento, ¿Qué tal si añadimos unos cuantos campos que nos permitan crear una ficha de producto?

¿Recuerdas que prometí explicarte el uso de $callback en el primer punto de este tutorial? Ahora vamos a crear una función con ese mismo nombre, que será la encargada de mostrar el contenido de nuestro metabox.

function dariobf_meta_box_cb( $post )
{
    ?>
	<p>Aquí pondremos todo el contenido de nuestro metabox</p>
    <?php
}

custom metabox

Añadiendo campos de texto (Input text)

Partiendo de la función mostrada anteriormente, podemos añadir campos de texto a nuestro metabox, adaptándola de la siguiente forma para, por ejemplo, recoger el ISBN del libro en cuestión:

function dariobf_meta_box_cb( $post )
{
?>
<p>
<label for="my_meta_box_text">ISBN</label>
<input type="text" name="my_meta_box_text" id="my_meta_box_text" value="<?php echo $text; ?/>" />
</p>
<?php
}

metabox

Si cada vez que editemos un libro (recuerda que nuestro ejemplo va sobre fichas de libros) queremos recuperar esta información tendremos que utilizar la función get_post_meta, así que vamos a añadir unas variables al ejemplo anterior que recuperarán esta información de la base de datos cada vez que carguemos la página de edición del libro.

function dariobf_meta_box_cb( $post )
{
	$values = get_post_custom( $post->ID );
	$text = isset( $values['my_meta_box_text'] ) ? esc_attr( $values['my_meta_box_text'][0] ) : '';
 
	// We'll use this nonce field later on when saving.
    wp_nonce_field( 'my_meta_box_nonce', 'meta_box_nonce' );
    ?>
	<p>
		<label for="my_meta_box_text">ISBN</label>
		<input type="text" name="my_meta_box_text" id="my_meta_box_text" value="<?php echo $text; ?/>" />
    </p>
    <?php
}

Con este añadido lo que hacemos es recoger la información del post actual en la variable $values y, valiéndonos de esta, el valor del input ISBN (en este ejemplo) en la variable $text. Como puedes observar, también insertamos ese contenido recogido en $text dentro del value del input, lo que mostrará el contenido recogido anteriormente en dicho input.

Añadiendo un selector desplegable (Select)

Por razones lógicas, en ocasiones necesitaremos darle al editor de la página la posibilidad de seleccionar una opción entre varias, por lo que vamos a añadirle a nuestra ficha de libro dos campo select para, por ejemplo, el tipo de tapa de encuadernación de libro (Rústica, pegada o cosida) y para el tipo de tapa (dura o blanda).

function dariobf_meta_box_cb( $post )
{
	$values = get_post_custom( $post->ID );
	$text = isset( $values['my_meta_box_text'] ) ? esc_attr( $values['my_meta_box_text'][0] ) : '';
	$selected = isset( $values['my_meta_box_select'] ) ? esc_attr( $values['my_meta_box_select'][0] ) : '';
	$selected2 = isset( $values['my_meta_box_select_2'] ) ? esc_attr( $values['my_meta_box_select_2'][0] ) : '';
	// We'll use this nonce field later on when saving.
    wp_nonce_field( 'my_meta_box_nonce', 'meta_box_nonce' );
    ?>
	<p>
		<label for="my_meta_box_text">ISBN</label>
		<input type="text" name="my_meta_box_text" id="my_meta_box_text" value="<?php echo $text; ?/>" />
    </p>
    <p>
		<label for="my_meta_box_select">Tipo de encuadernación</label>
		<select name="my_meta_box_select" id="my_meta_box_select">
			<option value="rustica" <?php selected( $selected, 'rustica' ); ?>>Rústica</option>
			<option value="pegada" <?php selected( $selected, 'pegada' ); ?>>Pegada</option>
			<option value="cosida" <?php selected( $selected, 'cosida' ); ?>>Cosida</option>
		</select>
	</p>
	<p>
		<label for="my_meta_box_select_2">Tipo de Tapa</label>
		<select name="my_meta_box_select_2" id="my_meta_box_select_2">
			<option value="dura" <?php selected( $selected2, 'dura' ); ?>>Dura</option>
			<option value="blanda" <?php selected( $selected2, 'blanda' ); ?>>Blanda</option>
		</select>
	</p>
    <?php
}

La función va cogiendo volumen, vamos por partes.

custom metabox

¿Vas cogiendo la idea? Con este sistema podemos añadir tantos campos como queramos, del tipo que queramos… Ahí va otro ejemplo más:

Añadiendo Check-box

A continuación, introduciré un campo checkbox, que me permitirá marcar si hay stock o no del libro en cuestión.

function dariobf_meta_box_cb( $post )
{
	$values = get_post_custom( $post->ID );
	$text = isset( $values['my_meta_box_text'] ) ? esc_attr( $values['my_meta_box_text'][0] ) : '';
	$selected = isset( $values['my_meta_box_select'] ) ? esc_attr( $values['my_meta_box_select'][0] ) : '';
	$selected2 = isset( $values['my_meta_box_select_2'] ) ? esc_attr( $values['my_meta_box_select_2'][0] ) : '';
	$check = isset( $values['my_meta_box_check'] ) ? esc_attr( $values['my_meta_box_check'] ) : '';
	// We'll use this nonce field later on when saving.
    wp_nonce_field( 'my_meta_box_nonce', 'meta_box_nonce' );
    ?>
	<p>
		<label for="my_meta_box_text">ISBN</label>
		<input type="text" name="my_meta_box_text" id="my_meta_box_text" value="<?php echo $text; ?/>" />
    </p>
    <p>
		<label for="my_meta_box_select">Tipo de encuadernación</label>
		<select name="my_meta_box_select" id="my_meta_box_select">
			<option value="rustica" <?php selected( $selected, 'rustica' ); ?>>Rústica</option>
			<option value="pegada" <?php selected( $selected, 'pegada' ); ?>>Pegada</option>
			<option value="cosida" <?php selected( $selected, 'cosida' ); ?>>Cosida</option>
		</select>
	</p>
	<p>
		<label for="my_meta_box_select_2">Tipo de Tapa</label>
		<select name="my_meta_box_select_2" id="my_meta_box_select_2">
			<option value="dura" <?php selected( $selected2, 'dura' ); ?>>Dura</option>
			<option value="blanda" <?php selected( $selected2, 'blanda' ); ?>>Blanda</option>
		</select>
	</p>
	<p>
		<input type="checkbox" id="my_meta_box_check" name="my_meta_box_check" <?php checked( $check, 'on' ); ?/> />
		<label for="my_meta_box_check">Libro disponible en Stock</label>
    </p>
    <?php
}

Al igual que con el input y el select, hemos agregado una variable que recoge la información de la base de datos y el propio checkbox. Creo que la idea se ha entendido; si no es así vuelve al punto donde explico cómo introducir el input text y el select.

Guardando los datos

Resumiendo hasta ahora, hemos creado un metabox con varios campos que añaden información adicional a la ficha de los libros. A ese metabox le hemos dado forma con una función donde, además, hemos dejado listas unas cuantas variables que recogerán esa información de la base de datos cada vez que decidamos editar la ficha de un libro.

Pero… ¿En qué momento hemos guardado los datos? ¿Funciona por arte de magia? La respuesta, tras leer el encabezado que precede a este texto, es obvio que no; no hemos guardado los datos en ningún sitio, por lo que nuestro metabox todavía no es funcional.

Para guardar los datos necesitamos enganchar con un hook nuestra función al save_post original de WordPress. Tranquilo, no te voy a pedir que entiendas cómo funciona este proceso, pero sí quiero que sepas que eso se hace con la función add_action de WordPress:

add_action( 'save_post', 'dariobf_meta_box_save' );

Aún no hemos programado la función dariobf_meta_box_save, que será la encargada de guardar nuestros datos.

Esta función recibirá un argumento, el id del post, y se encargará, como digo, de guardar todos los datos de nuestro metabox.

El hook save_post ejecutará esta función siempre pulsemos el botón “Guardar borrador” o “Actualizar” (también “Publicar”) en la página de edición.

Antes de continuar, necesitamos verificar tres cosas: si la entrada se está autoguardando, verificar el valor nonce que hemos creado antes y comprobar que el usuario actual puede realmente modificar este contenido.

add_action( 'save_post', 'dariobf_meta_box_save' );
function dariobf_meta_box_save( $post_id )
{
    // Bail if we're doing an auto save  
    if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
 
    // if our nonce isn't there, or we can't verify it, bail 
    if( !isset( $_POST['meta_box_nonce'] ) || !wp_verify_nonce( $_POST['meta_box_nonce'], 'my_meta_box_nonce' ) ) return;
 
    // if our current user can't edit this post, bail  
    if( !current_user_can( 'edit_post' ) ) return;
}

Y, por fin, viene lo divertido: guardar los datos. Como primera regla a la hora de poner cualquier información en una base de datos diría que jamás te fíes del usuario. Incluso si ese usuario es tu hermano, hermana o padre. Es más, incluso si ese usuario eres tú mismo, nunca te fíes de lo que intentan introducir en tu base de datos. Por esto, antes de guardar los datos vamos a comprobar que no hay nada malicioso en los datos. Afortunadamente WordPress cuenta con sistemas que controlan esto por nosotros.

Ya hemos utilizado antes la función esc_attr(). Esta función codifica las comillas simples y dobles así como los símbolos mayor qué y menor qué en HTML. Puedes leer más sobre esta función aquí.

Vamos a utilizar la función update_post_meta para guardar nuestros datos. Esta función recibe tres argumentos: el ID del post, la “llave” (key) del meta y el valor.

add_action( 'save_post', 'dariobf_meta_box_save' );
function dariobf_meta_box_save( $post_id )
{
    // Bail if we're doing an auto save  
    if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
 
    // if our nonce isn't there, or we can't verify it, bail 
    if( !isset( $_POST['meta_box_nonce'] ) || !wp_verify_nonce( $_POST['meta_box_nonce'], 'my_meta_box_nonce' ) ) return;
 
    // if our current user can't edit this post, bail  
    if( !current_user_can( 'edit_post' ) ) return;
 
    // now we can actually save the data  
    $allowed = array(
        'a' => array( // on allow a tags  
            'href' => array() // and those anchors can only have href attribute  
        )
    );
 
    // Make sure your data is set before trying to save it  
    if( isset( $_POST['my_meta_box_text'] ) )
        update_post_meta( $post_id, 'my_meta_box_text', wp_kses( $_POST['my_meta_box_text'], $allowed ) );
 
    if( isset( $_POST['my_meta_box_select'] ) )
        update_post_meta( $post_id, 'my_meta_box_select', esc_attr( $_POST['my_meta_box_select'] ) );
 
	if( isset( $_POST['my_meta_box_select_2'] ) )
        update_post_meta( $post_id, 'my_meta_box_select_2', esc_attr( $_POST['my_meta_box_select_2'] ) );
 
    // This is purely my personal preference for saving check-boxes  
    $check = isset( $_POST['my_meta_box_check'] ) && $_POST['my_meta_box_select'] ? 'on' : 'off';
    update_post_meta( $post_id, 'my_meta_box_check', $check );
}

custom metaboxes

Con esto ya tenemos un metabox donde recoger nuestros campos. En otra entrada explicaré cómo mostrar estos datos en el front-end para mostrar la ficha de producto de una forma chula.

¿Cómo muestro los datos de un metabox en el Front-End?

Es sencillo, sólo tienes que utilizar get_post_meta o get_post_custom.

Si te ha gustado no dudes en compartirlo con tus amigos y exponer todas tus dudas o agradecimientos en los comentarios.

Puedes obtener más información sobre metaboxes en Tuts+ o el códex

30 respuestas a “Metaboxes en WordPress, cómo crearlos”

  1. Ruben dice:

    Interesante, ¿en qué proyecto fue?, ¿está online?

    Por cierto un libro sin autor no es un libro xD

    • Darío BF dice:

      Es un proyecto propio que lanzaré próximamente. Lo verás en cuanto salga ;-)

      Ya sabes, mete un campo tipo text para recoger el autor :-P

      Esto tiene una pila posibilidades… jaja

  2. Buenísimo Diario. Muy completo, felicidades!
    Para los metabox siempre había utilizado el CMB (Custom Metaboxes and Fields) como framework, pero imagino que al final estás cargando mucho más código de lo necesario.

    Con lo que cuentas creo que se optimiza más si solo quieres un par de campos extras en un número pequeño de custom post types. Muy bueno repito, me lo guardo para el próximo proyecto!

    Saludos :-)

    • Darío BF dice:

      Todo lo que sea evitarle cargas al servidor, o al propio visitante es mejor. Con esto controlas exactamente qué añades y da un control total.

      ¡Me alegro que te guste Ricardo! :-D

  3. Es muy interesante. Lo que me queda es investigar como realizar las búsquedas y ordenar a partir de los metacampos.

    Gracias!

  4. Isaac dice:

    Sos el puto amo de WordPress… Sabelo

  5. César C. dice:

    Buenas,

    en el add_action usas dariobf_metabox

    add_action( 'add_meta_boxes', 'dariobf_metabox' );

    y luego el nombre de función es cd_meta_box_add.

    function cd_meta_box_add() {}

    ¿La función no debería llamarse dariobf_metabox?

  6. iLen dice:

    Hola, muchas gracias por tu explicación la cual ya pude agragar un metabox dinamico en mi framework para facil uso :)

  7. Elvin dice:

    gracias por tus aportes, son muy buenos tus tutoriales, una consulta porfavor, yo ya agregué los metaboxes puse algunos campos, algo parecido al tuyo, como podría mostrar estos datos en front-end, te lo agradecería bastante si me ayudarías con eso, de ante mano una vez más, muchas gracias por aportar tus conocimientos

  8. Jhonnatan Diaz dice:

    Porque cuando agrego el código al final del archivo functions.php, la página se pone blanco, solo me ha funcionado la primera parte del tutorial con los custom post types, luego intento agregar algo y se me pone la página en blanco y se me bloquea todo, gracias

    • DarioBF dice:

      Hola Jhonnatan, lee bien el tutorial, seguramente estés copiando cosas sin leer y haciéndolo mal. Te aseguro que el código funciona.

  9. OscarB dice:

    Hola Dario,primero que nada, muchas gracias por compartir estos valiosos recursos.

    Te queria preguntar si es posible asignar un metabox a post de una categoria y de ser posible como se deberia hacer.

    He tratado de hacerlo ingresando por ejemplo el slug en vez de ‘page’ o ‘post’, pero no he logrado el objetivo.

  10. Emanuel dice:

    Es posible agregar esto a una categoria, es decir no agregarlo a los post sino a la edicion de una categoria. ??

  11. Fran dice:

    Hola,

    Muchas gracias. Un post realmente útil. Solo tengo una duda. ¿qué habría que hacer si queremos que el select admita valores múltiples?.

  12. Franco dice:

    Hola.
    Muchas gracias por explicarlo tambien me sirvio de mucho.

  13. Eric dice:

    Hola AMigo, gracias por sacar tiempo y compartir sabiduria, Mi pregunta de persona nueva en el tema, en que archivo o dentro de que carpeta se coloca este codigo?

  14. Giuliano dice:

    Hola gracias por el tutorial. Donde se guardan los datos de los campos? osea los valores de texto de los inputs, en la bd? Osea no entiendo como funciona el metodo save… Y ademas no sabes donde puedo encontrar un tutorial sobre agregar una imagen de perfil..

  15. frank dice:

    hola, estoy recien empezando en esto de wordpress, osea aprendiendo a crear paginas y eso, veo que explicas muy bien me gustaria que tuvieras postcat creo que seria mas entendible y porque no un curso de creacion de paginas con wordpress con funciones avanzadas, como por ejemplo ya hay paginas de anuncioss clasificados en wordpress y esas paginas requieren de muchas funcionalidades y meta-box, gracias por tus aportes.

  16. Patricio dice:

    Hola, en la función:
    add_meta_box( ‘my-meta-box-id’, ‘Datos adicionales del libro’, ‘dariobf_metabox’, ‘libro’, ‘normal’, ‘high’ );

    ¿Debería ser?
    add_meta_box( ‘my-meta-box-id’, ‘Datos adicionales del libro’, ‘dariobf_meta_box_cb’, ‘libro’, ‘normal’, ‘high’ );

    Saludos.

  17. […] PREVIA: Este artículo se fundamenta, basicamente, en el artículo Metaboxes en WordPress, cómo crearlos, de DaríoBF, y, por supuesto, el Codex […]

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *