Para todo llega un día, y todos los que trabajamos con WordPress sabemos que más tarde o más temprano tendremos que representar la información de nuestra web en forma de Custom Post Type, o entrada personalizada. Si las entradas que queremos mostrar constaran de un título, una descripción y una imagen destacada, no tendríamos que hacer nada más y aquí terminaría este post. Pero en muchas ocasiones, y digo muchas…., sucede que además de eso, queremos mostrar una fecha, o un estado, o una galería de imágenes, o cualquier otra cosa que nos pase por la imaginación. ¿Cómo conseguimos que nuestra entrada personalizada, además de tener un título, una descripción y una imagen destacada, sea capaz de representar más información y en la forma que yo quiera? ¿Cómo consigo añadir campos personalizados a los custom post types, para luego mostrarlos en la web?.
Para quien no sepa que es un custom post type, en una publicación anterior hablo de ello y además existe una documentación muy extensa en el codex de WordPress que habla de él, pero para quien no tenga tiempo de leer tanta información: un custom post type – o entrada personalizada – es el bloque de información que maneja WordPress para ser representado en el front-end de tu sitio web. Cuando creamos un blog usamos entradas que constan de titulo, descripción e imagen destacada. Del mismo modo, cuando creamos una entrada personalizada del tipo: Película, también contamos con la misma información: titulo, descripción e imagen destacada, que podemos mostrar en nuestra página web. Pero, y si quisiéramos incluir a las películas información sobre el autor, o fecha de estreno, o un trailer…. En este último caso tendríamos que añadir campos personalizados para que desde el back-end, se pueda incluir la información que se va a consumir desde el front-end. Y es de esto precisamente de lo que quiero hablaros, como añadir campos personalizados (meta-datos) a nuestras entradas, posts o custom post types, para mostrarlos en el front-end.
Planteamiento del problema e identificando elementos
Queremos añadir campos personalizados al backend de wordpress. Supongamos que decidimos crear un custom post type ‘Cliente’ y dentro de él creamos tres entradas, tal y como muestra la siguiente imágen:

Cada cliente representa un proyecto – o trabajo -, que almacena la lista de tareas que se han llevado a cabo con el cliente. Así, el cliente ‘La pedrera blanca’ mostrará la siguiente interfaz:

¿Cómo añadimos un metabox?
Para añadir un metabox, debemos utilizar la función add_meta_box, que según la documentación oficial recibe la siguiente información:
add_meta_box( $id, $title, $callback, $page, $context, $priority, $callback_args );
- $id: Es un identificador único de nuestro metabox. Nos será útil en caso de que queramos aplicarle estilos con CSS o vayamos a usarlo desde nuestro código javaScript.
- $title: Es el nombre que le damos a nuestro metabox y será el título que nos aparecerá en la caja.
- $callback: Es la función que usaremos para configurar nuestro metabox.
- $page: Define el lugar donde queremos mostrar nuestro metabox. Puede ser un página, un post o una entrada personalizada como en nuestro caso.
- $context: Define el lugar donde queremos mostrar nuestro metabox. Por ejemplo: «normal» nos lo mostraría debajo del editor de la entrada o página, «side» lo colocaría en la barra lateral de la página de edición y «advanced» lo colocaría en la misma columna que el editor.
- $priority: Puede tener los valores: «hight», «default» o «low», y lo que indica es si queremos mostrar nuestro metabox arriba o debajo de la pantalla.
- $callback_args: Este argumento es opcional y sirve para añadir si la función que usaremos para nuestro metabox llevará argumentos.
En nuestro ejemplo, la función quedaría como sigue:
add_action('add_meta_boxes','add_metabox_section');
function add_metabox_section(){
add_meta_box('my_custom_metabox', 'List of tasks','generate_metabox_html','cliente', 'normal', 'low');
}
Cabe destacar que el hook ‘add_meta_boxes’, según la documentación oficial, permite el registro de cualquier metabox en la página de edición de cualquier entrada.
GENERANDO EL FORMULARIO CON LOS CUSTOM FIELDS
La función generate_metabox_html es la responsable de mostrar los campos de formulario en la página de edición de la entrada. Este tipo de funciones, siempre suele llevar la misma estructura:
- Declaramos la variable global $post o lo pasamos como parámetro a la función, en ambos casos es lo mismo. En nuestro ejemplo, la variable $post se ha declarado como variable global.
- Obtenemos los datos en caso de que existan y que nos lo muestre en los campos del formulario. En nuestro ejemplo cada elemento que leemos es un array.
- Construimos el formulario con los campos que nos interesan e inicializando cada campo con su valor.
function generate_metabox_html(){
//Declaramos una variable global
global $post;
//Leemos los datos en caso de que existan
$startDate = get_post_meta($post->ID, 'startDate', true);
$endDate = get_post_meta($post->ID, 'endDate', true);
$idTask= get_post_meta($post->ID,'idTask',true);
$taskName= get_post_meta($post->ID,'taskName',true);
$taskProgress=get_post_meta($post->ID,'taskProgress',true);
$taskDependency=get_post_meta($post->ID,'taskDependency',true);
?>
<div class="input_fields_wrap">
<a class="add_field_button button-secondary">Añadir tarea</a>
<?php
//Si los datos existen y son arrays mostramos construimos el formulario e inicializamos
if(isset($startDate) && is_array($startDate) ||
isset($endDate) && is_array($endDate) ||
isset($idTask) && is_array($idTask) ||
isset($taskName) && is_array($taskName) ||
isset($taskProgress) && is_array($taskProgress) ||
isset($taskDependency) && is_array($taskDependency)) {
$i = 1;
$output = '';
for ($i=0;$i<count($startDate);$i++){
$output = '<div class="flex"><div><label>Fecha Inicio: <input type="date" name="startDate[]" value="' . $startDate[$i] . '"></label></div><div><label>Fecha Fin:<input type="date" name="endDate[]" value="' . $endDate[$i] . '"></label></div> <div><label>Nombre:<input type="text" name="idTask[]" value="' . $idTask[$i] . '"></label></div> <div><label>Descripcion:<input type="text" name="taskName[]" value="' . $taskName[$i] . '"></label></div> <div><label>Progreso:<input type="range" min="1" max="100" name="taskProgress[]" value="' . $taskProgress[$i] . '"></label></div><div><label>Esta tarea depende de: <input type="text" name="taskDependency[]" value="' . $taskDependency[$i] . '"></label></div>';
if ($i!==0 && $i>0)
$output .= '<a href="#" class="remove_field"> Eliminar</a></div>';
else $output .= '</div>';
echo $output;
}
} else {
//En caso de que abramos la entrada por primera vez, construimos el formulario con campos vacios
echo '<div class="flex"><div><label>Fecha Inicio: <input type="date" name="startDate[]"></label></div> <div><label>Fecha Fin:<input type="date" name="endDate[]"></label></div> <div><label>Nombre:<input type="text" name="idTask[]"></label></div> <div><label>Descripcion:<input type="text" name="taskName[]"></label></div> <div><label>Progreso: <input type="range" min="1" max="100" name="taskProgress[]"></label></div><div><label>Esta tarea depende de:<input type="text" name="taskDependency[]"></label></div></div>';
}
?>
</div>
<?php
}
Guardar los datos en forma de custom fields
A estas alturas, ya deberíamos tener creada la interfaz de usuario que consiste en un metabox con varios campos de formulario que añaden información al cliente en forma de custom fields, pero no hemos configurado como guardar los datos.
Para guardar los datos usaremos el hook save_post para que ejecute la función save_my_post_meta, donde procederemos a guardar los datos. El código es el siguiente:
add_action('save_post', 'save_my_post_meta');
function save_my_post_meta($post_id) {
// Comprueba si hacemos un autoguardado
if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
// Comprobamos si el usuario tiene permisos para modificar este post
if( !current_user_can( 'edit_post' ) ) return;
// Si existen valores, actualizamos el post meta
if(isset($_POST['startDate']) && isset($_POST['endDate'])) {
// $post_id, $meta_key, $meta_value
update_post_meta( $post_id, 'startDate', $_POST['startDate'] );
update_post_meta( $post_id, 'endDate', $_POST['endDate'] );
update_post_meta( $post_id, 'idTask', $_POST['idTask'] );
update_post_meta( $post_id, 'taskName', $_POST['taskName'] );
update_post_meta( $post_id, 'taskProgress', $_POST['taskProgress'] );
update_post_meta( $post_id, 'taskDependency', $_POST['taskDependency'] );
}
}
La función implicada en el guardado de los datos es update_post_meta y su funcionamiento es bastante intuitivo, pues recibe tres parámetros:
- $post_id: Para indicar a que post afecta.
- $nombre del custom field: El nombre que pongamos aquí se creará en la base de datos y almacenará la información que pasemos a través del formulario.
- $_POST[‘nombre_campo’]: Aquí pasamos el nombre del campo del formulario que recoge el valor que queremos guardar.
Añadir jQuery a la página de edición del post
Tal y como se ha planteado el problema inicial, el formulario es dinámico, es decir, cada vez que el usuario haga click en el botón añadir, se creará otra lista de campos de formulario para que permita añadir más información. Además, también se pueden eliminar campos de formulario. Esta función debemos programarla usando javascript.
Para poder añadir javascript a nuestra página de edición de entradas, necesitamos usar el hook ‘admin_enqueue_scripts’, que ejecutará los scripts que necesitemos añadir. En nuestro caso añadiremos un script para el comportamiento y una hoja de estilos
add_action('admin_enqueue_scripts', 'admin_enqueue_scripts_func');
function admin_enqueue_scripts_func() {
//Añadimos script
wp_enqueue_script( 'my-script', get_template_directory_uri() . '/js/dynamic-fields.js', array( 'jquery' ), '20160816', false );
wp_enqueue_style('my-own-style', get_template_directory_uri(). '/style-admin.css');
}
Script javascript
jQuery(document).ready(function() {
var max_fields = 10; //Número máximo de campos de formulario
var wrapper = jQuery(".input_fields_wrap"); //Añadimos capa
var add_button = jQuery(".add_field_button"); //Añadimos boton
var x = 1; //Contador inicializado a 1
jQuery(add_button).click(function(e){ //Cuando hagamos click en botón añadir
e.preventDefault(); //Evitamos que el enlace se comporte como tal
if(x < max_fields){ //número máximo de cajas permitidas
x++;
jQuery(wrapper).append(
'<div class="flex">
<div><label>Fecha Inicio: <input type="date" name="startDate[]"></label></div>
<div><label>Fecha Fin:<input type="date" name="endDate[]"></label></div>
<div><label>Nombre:<input type="text" name="idTask[]"></label></div>
<div><label>Descripcion:<input type="text" name="taskName[]"></label></div>
<div><label>Progreso: <input type="range" min="1" max="100" name="taskProgress[]"></label></div>
<div><label>Esta tarea depende de:<input type="text" name="taskDependency[]"></label></div>
<a href="#" class="remove_field"> Eliminar</a></div>' ); //add input box
}//Fin del if
}); //Fin del evento click
jQuery(wrapper).on("click",".remove_field", function(e){ //El usuario hace click en borrar
e.preventDefault();
jQuery(this).parent('div').remove(); x--;
});
})
El código anterior, devolvería como resultado lo siguiente:

En conclusión, espero que este post os haya servido de ayuda a la hora de crear vuestros CPT y personalizarlos añadiendo metadatos (o custom fields), para luego representarlos en pantalla. La tarea de añadir campos personalizados al backend de wordpress, puede resultar compleja la primera vez que se hace, así que si necesitáis ayuda, contactar conmigo y os ayudaré.
Happycoding!