Cómo crear tipos y campos personalizados en WordPress

Siguiendo este tutorial verás cómo crear un plugin de WordPress para convertir tu web en algo más que un blog. Vas a crear tipos de contenido personalizado, en inglés «Custom Posts Type» o «CPT» que te permitirán convertir tu página en un recetario de cocinas, un gestor de tu colección de libros, películas o sellos de correo.

Y lo harás todo desde código, sin utilizar ningún plugin pues esta es la mejor manera de aprender como funciona realmente WordPress y como programar aplicaciones completas con él. Más adelante en algún proyecto quizás necesites por cuestiones de tiempo o presupuesto utilizar algún plugin para crear campos personalizados como Advanced Custom Fields pero tu ya sabrás como funciona internamente.

Requisitos previos

Para enfrentar este tutorial te recomiendo un conocimiento medio o avanzado de WordPress y unos conocimientos mínimos de PHP.

Empezarás por lo básico creando el esqueleto del plugin con lo mínimo para que funcione todo correctamente. En tutoriales más avanzados aprenderás a crear plugins utilizando un esqueleto algo más complejo pero más estandarizado. Si ya eres desarrollador de WordPress quizás eches en falta definir el plugin dentro de un objeto, separar en distintos archivos las distintas partes del plugin y otras buenas prácticas que no he utilizado aquí a propósito, porque me parece más didáctico este planteamiento. En sucesivos tutoriales iré incorporando todas esas cuestiones.

Así que busca un sitio tranquilo para realizar tu práctica, con un tiempo delimitado durante el que nada te moleste, cíñete el cinturón, respira hondo y saluda a tu adversario antes de comenzar el combate.

Comienza a crear el plugin

Parte de una instalación básica y limpia, con un tema cualquiera, aunque si quieres que tenga apariencia de recetario te recomiendo uno de estos dos que son gratuitos y del repositorio oficial:

Con tu editor de código favorito crea una nueva carpeta dentro de la ruta wp-content/plugins, la puedes llamar recetario o algo más acorde a lo que quieras hacer.

Dentro de esa carpeta crea un archivo y nómbralo recetario.php, este será el punto de partida de tu plugin. Es muy importante escribir correctamente la cabecera de este artículo para que WP lo identifique como un plugin.

<?php
/*
Plugin Name: Recetario
Author Name: Juanan Ruiz
Plugin URI: https://kungfupress.com/como-crear-tipos-y-campos-personalizados-en-wordpress
Description: Un plugin para aprender a programar WP practicando con un gestor de recetas.
*/

La única línea realmente imprescindible es la del Plugin Name, el resto es información adicional, útil pero no imprescindible a nivel de funcionamiento interno. Que no se te olvide tampoco la primera línea con la etiqueta <?php, si olvidas ponerla el contenido del plugin o una parte de él se verá por toda tu web (si no pasa algo peor).

Si ahora guardas el fichero y vas al listado de plugins desde el panel de administración de tu sitio WP verás tu nuevo plugin esperando y listo para ser activado.

Creando un Custom Post Type o Campo Personalizado

Este pobre plugin aún no sabe hacer nada, así que vas a comenzar a definir el Custom Post Type (CPT). Para ello puedes copiarlo de otro que ya tengas hecho, del código que tienes más abajo o generarlo desde la web GenerateWP. Para ello vas recorriendo las pestañas que aparecen en la página: General, Post Type, Labels, etc y rellenando los campos.

Herramienta GenerateWP

Empieza por la pestaña General y rellena Function Name con: receta_post_type
Este es el nombre de la función con la que vas a definir el CPT

Luego ve a la pestaña Post Type y rellena los siguientes campos:

  • Post Type Key: receta
  • Name (Singular): Receta
  • Description: Receta
  • Name (Plural): Recetas
  • Hierarchical: No

En la pestaña Labels se pueden configurar un montón de etiquetas, rellena sólo estas dos de momento:

  • Menu name: Recetas
  • Admin Bar Name: Receta

Por último en la pestaña Options marca la casilla Custom Fields y para terminar pulsa el botón Update Code que hay un poco más abajo, copia el código generado en tu plugin y te quedará algo parecido a esto.

<?php
/*
Plugin Name: Recetario
...
*/
​
// Register Custom Post Type
function receta_post_type() {
    $labels = array(
      'name' => _x( 'Recetas', 'Post Type General Name', 'text_domain' ),
      'singular_name' => _x( 'Receta', 'Post Type Singular Name', 'text_domain' ),
      'menu_name' => __( 'Recetas', 'text_domain' ),
      'name_admin_bar' => __( 'Receta', 'text_domain' ),
      'archives' => __( 'Item Archives', 'text_domain' ),
      'attributes' => __( 'Item Attributes', 'text_domain' ),
      'parent_item_colon' => __( 'Parent Item:', 'text_domain' ),
      'all_items' => __( 'All Items', 'text_domain' ),
      'add_new_item'          => __( 'Add New Item', 'text_domain' ),
      'add_new' => __( 'Add New', 'text_domain' ),
      'new_item' => __( 'New Item', 'text_domain' ),
      'edit_item' => __( 'Edit Item', 'text_domain' ),
      'update_item' => __( 'Update Item', 'text_domain' ),
      'view_item' => __( 'View Item', 'text_domain' ),
      'view_items' => __( 'View Items', 'text_domain' ),
      'search_items' => __( 'Search Item', 'text_domain' ),
      'not_found' => __( 'Not found', 'text_domain' ),
      'not_found_in_trash' => __( 'Not found in Trash', 'text_domain' ),
      'featured_image' => __( 'Featured Image', 'text_domain' ),
      'set_featured_image'=> __( 'Set featured image', 'text_domain' ),
      'remove_featured_image' => __( 'Remove featured image', 'text_domain' ),
      'use_featured_image' => __( 'Use as featured image', 'text_domain' ),
      'insert_into_item' => __( 'Insert into item', 'text_domain' ),
      'uploaded_to_this_item' => __( 'Uploaded to this item', 'text_domain' ),
      'items_list' => __( 'Items list', 'text_domain' ),
      'items_list_navigation' => __( 'Items list navigation', 'text_domain' ),
        'filter_items_list' => __( 'Filter items list', 'text_domain' ),
    );
    $args = array(
      'label'                 => __( 'Receta', 'text_domain' ),
      'description'           => __( 'Receta', 'text_domain' ),
      'labels'                => $labels,
      'supports'              => array( 'title', 'editor', 'thumbnail', 'comments', 'revisions', 'custom-fields' ),
      'hierarchical'          => false,
      'public'                => true,
      'show_ui'               => true,
      'show_in_menu'          => true,
      'menu_position'         => 5,
      'show_in_admin_bar'     => true,
      'show_in_nav_menus'     => true,
      'can_export'            => true,
      'has_archive'           => true,
      'exclude_from_search'   => false,
      'publicly_queryable'    => true,
      'capability_type'       => 'page',
    );
    register_post_type( 'receta', $args );
    // Hay que ejecutar este código para que funcionen los permalinks al custom post type
    // Es un poco rebuscado porque interesa que sólo se ejecute una vez
    $set = get_option( 'post_type_rules_flased_vivienda' );
    if ( true !== $set ) {
        flush_rewrite_rules( false );
        update_option( 'post_type_rules_flased_vivienda', true );
    }
}
add_action( 'init', 'receta_post_type', 0 );

¿Cómo se ha definido el CPT con este código?

Primero se ha creado un array llamado $labels que contiene las etiquetas para nombrar los diferentes elementos y acciones del nuevo CPT.

Luego se ha definido otro array $args, que a su vez ha incluido a $labels , y en este segundo array se pasan todos los parametros para configurar el comportamiento del CPT. Cosas como si va a ser público, si va a aparecer en el menú de administración, si se va a comportar como una página o como una entrada, si los contenidos se van a incluir en las búsquedas del sitio, etc.

Una vez definido este segundo array se llama a la función register_post_type que requiere dos parámetros, uno con el nombre clave del CPT y otro con el nombre del array de argumentos.

Por último, fuera ya de la función se engancha esta función que define al CPT al hook add_action(init) que es el que se invoca cuando se carga el plugin

Guarda ahora el archivo y ve de nuevo al listado de plugins en el panel de administración. Busca el plugin y ahora puedes darle a activar. Si todo ha ido bien en el menú de la izquierda aparecerá un nuevo elemento con el nombre «Recetas». Si haces clic en Add New verás que aparece algo parecido a una nueva entrada de blog.

¡Felicidades por haber llegado hasta aquí! Has creado un nuevo tipo de contenido para WordPress, no muchos lo han hecho.

Verás que hay algunos títulos y nombres de menú sin actualizar, abre de nuevo el archivo del plugin y traduce los elementos que estimes necesarios, por ejemplo All Items, Add New, Add New Item, etc. La próxima vez esto puedes hacerlo directamente editando las Labels en el WordPress Type Generator.

Los campos personalizados

Llegamos a un punto crítico.

Un tipo de contenido personalizado tiene poco sentido si no tiene también asociados algunos campos personalizados. En este ejemplo estaría bien contar con un campo para ingredientes, otro para instrucciones, tiempo de preparación, comensales, tipo de cocina, etc. A esto se le llama «Custom Fields», se pueden crear a mano dentro de cada receta pero hay que crearlo cada vez y es muy engorroso.

Existe un plugin fantástico para hacer esto Advanced Custom Fields (ACF), está genial y te recomiendo tenerlo en tu caja de herramientas . Pero la idea de este tutorial es ir más alla y crear también los Campos Personalizados desde código, este es un sendero poco transitado pero que te recomiendo recorrer si quieres convertirte en un verdadero guerrero de WordPress.

Así que a partir de aquí tienes dos opciones, utilizar la vía rápida del plugin ACF o seguir aprendiendo a programar como un auténtico practicante de la disciplina.

¿Sigues conmigo? ¡Bravo! Este camino es duro pero te garantiza grandes satisfacciones.

Agregando meta boxes.

Los meta boxes son todas esas cajas de formularios que rodean normalmente el contenido de un artículo o de una página cuando estás utilizando el editor de WP. Aquí los vas a utilizar para colocar en ellos los elementos de formulario donde recogerás tus campos personalizados.

Como no podía ser menos WordPress tiene funciones para que puedas crear tus propias meta boxes. Dentro de ellas se puede colocar cualquier tipo de información: texto, imágenes, etc y también se pueden introducir campos de formulario para recoger valores.

Empieza agregando al plugin una función parar registrar una meta box con un par de campos personalizados, luego podrás añadir más. Dentro de esa función vas a llamar a la función add_meta_box de WordPress.

add_meta_box($id, $title, $callback, $screen, $context, $priority, $callback_args);

  • $id: identificador o «slug» del nuevo «meta box».
  • $title: título que se mostrará en el «meta box».
  • $callback: función que imprime el contenido de la «meta box», se utiliza echo para generar la salida
  • $screen: representa la pantalla de administración en la que se mostrará esta «meta box». En este caso debes poner «receta» para que quede asociado a este «Custom Post Type».
  • $context y $priority: en este caso las configuras como «normal» y «high» respectivamente.

Verás que la función encargada de cargar el meta box (o los meta boxes si fueran varios) tiene un nombre un poco largo: kfp_receta_register_meta_boxes.

Lo he puesto así porque con la estructura que estamos usando para construir el plugin los nombres de función podrían entrar en conflicto con otros plugins. Imagina que has instalado otro plugin de recetas y el autor llama a su función «receta_register_meta_boxes», en este caso ambos plugins dejarían de funcionar. Esta forma de nombrar funciones y variables es un estándar y consiste en agregar al principio el nombre o las siglas del desarrollador (en mi caso son las siglas de Kung Fu Press), seguido del nombre o alias del plugin (en esta caso «receta») y luego viene el resto del nombre que quieras dar a la función. Si hubiéramos encapsulado el plugin en un objeto esto no sería necesario, veremos esta forma de trabajar en algún tutorial más avanzado.

Este es el código que tienes que añadir a tu plugin:

 /**
 * Agrega los meta boxes para el Custom Post Type Receta
 * https://developer.wordpress.org/reference/functions/add_meta_box/
 */
function kfp_receta_register_meta_boxes()
{
    add_meta_box('receta-info', 'Información', 'kfp_receta_output_meta_box', 'receta', 'normal', 'high');
}
​
/**
 * Genera el contenido que hay que mostrar dentro del meta box
 * @param WP_Post $post WordPress Post object
 */
function kfp_receta_output_meta_box($post)
{
}
​
add_action('add_meta_boxes', 'kfp_receta_register_meta_boxes');

Ahora puedes ir a tu Custom Post Type en el panel de administración y verás que cuando creas o editas un contenido de este tipo ahora aparece una meta box con el título «Información».

A continuación tienes que rellenar el contenido de esa meta box para que sirva para algo. Para eso escribirás algo que genere una salida en pantalla dentro de la función «kfp_receta_output_meta_box», utilizando la función echo de PHP.

/**
 * Genera el contenido que hay que mostrar dentro del meta box
 * @param WP_Post $post WordPress Post object
 */
function kfp_receta_output_meta_box($post)
{
    echo('<label for="tiempo_preparacion">' . __('Tiempo de preparación', 'text_domain') 
. '</label>');
    echo('<input type="text" name="tiempo_preparacion" id="tiempo_preparacion" value="">');
    echo('<p><label for="comensales">' . __('Comensales', 'text_domain') . '</label>');
    echo('<input type="date" name="comensales" id="fecha" value="">');
}

De nuevo te animo a probar el resultado en tu panel de administración para que veas que aparecen dos campos de formulario dentro de la meta box. En esta punto si intentas grabar alguna información ahí verás que no se guarda, disculpa que vaya pasito a paso pero quiero asegurarme que lo entiendes bien.

Ahora vas a añadir el código para que la información se grabe en la base de datos y para que se muestre en la meta box cada vez que abramos este registro.

Es importante comprobar que el usuario actual tiene permisos para grabar esta información y añadir también una cláusula de seguridad para evitar que alguien intente grabar información en esos campos si no viene de este formulario. Para esto último vas a usar la función wp_nonce_field que genera un campo oculto en el formulario, campo que luego comprobaremos antes de grabar la información.

/**
 * Genera el contenido que hay que mostrar dentro del meta box
 * @param WP_Post $post WordPress Post object
 */
function kfp_receta_output_meta_box($post)
{
    // Los campos se graban en la base de datos con un subrayado bajo como prefijo
    // WP indica así por defecto que son campos metas
    $tiempo_preparacion = get_post_meta($post->ID, '_tiempo_preparacion', true);
    $comensales = get_post_meta($post->ID, '_comensales', true);
    wp_nonce_field( 'graba_receta', 'receta_nonce' );
    echo('<label for="tiempo_preparacion">' . __('Tiempo de preparación', 'text_domain') . '</label>');
    echo('<input type="text" name="tiempo_preparacion" id="tiempo_preparacion" value="' . esc_attr($tiempo_preparacion) . '">');
    echo('<p><label for="comensales">' . __('Comensales', 'text_domain') . '</label>');
    echo('<input type="number" name="comensales" id="comensales" value="' . esc_attr($comensales) . '">');
}
​
/**
 * Graba los campos del formulario del meta box
 * @param int $post_id Post ID
 * @return bool|int
 */
function kfp_receta_save_meta_boxes($post_id)
{
    // Comprueba que el nonce es correcto para evitar ataques CSRF
    if ( !isset($_POST['receta_nonce']) || ! wp_verify_nonce( $_POST['receta_nonce'], 'graba_receta') ) {
        return $post_id;
    }
​
    // Comprueba que el tipo de post es receta
    if ('receta' != $_POST['post_type']) {
        return $post_id;
    }
​
    // Comprueba que el usuario actual tiene permiso para editar esto
    if (!current_user_can('edit_post', $post_id)) {
        return $post_id;
    }
​
    // Ahora puedes grabar los datos con tranquilidad
    // Observa que cuando vienen del formulario los campos meta no vienen con el prefijo subrayado bajo pero cuando los grabas en el post hay que poner el prefijo, igual que cuando los leías del post
    $tiempo_preparacion = sanitize_text_field($_POST['tiempo_preparacion']);
    update_post_meta($post_id, '_tiempo_preparacion', $tiempo_preparacion);
    $comensales = sanitize_text_field($_POST['comensales']);
    update_post_meta($post_id, '_comensales', $comensales);
    return true;
}
​
add_action('save_post', 'kfp_receta_save_meta_boxes');

Con esto ya debe funcionar bien la grabación de los campos meta, prueba a rellenar los campos personalizados en tus recetas y al guardarla, si no hay ningún error, deben permanecer los valores que has introducido

Y por último habrá que mostrar esos campos que tanto esfuerzo han costado en el front end, para que puedan consultarlo tus visitantes. Esta es una solución un poco burda, pero no quiero que se alargue más el artículo metiendo conceptos relacionados con plantillas, que pondrás ver con más detenimiento en otros tutoriales. Así que lo vas a hacer también con código y desde el propio plugin.

Usarás el hook «the_content» que es el que se llama cada vez que se va a imprimir el contenido de cualquier entrada, página o post personalizado. Lo que vas a hacer es añadir al contenido principal del artículo los campos personalizados que te interesen.

function kfp_receta_add_custom_fields_to_content( $content ) 
{
    $custom_fields = get_post_custom();
    $content .= "<ul>";
    if( isset( $custom_fields['_tiempo_preparacion'] ) ) {
        $content .= '<li>Tiempo de preparación: '. $custom_fields['_tiempo_preparacion'][0] . '</li>';
    }
    if( isset( $custom_fields['_comensales'] ) ) {
        $content .= '<li>Comensales: ' . $custom_fields['_comensales'][0] . '</li>';
    }
    $content .= '</ul>';
​
    return $content;
}
​
add_filter( 'the_content', 'kfp_receta_add_custom_fields_to_content' )

Como mostrar a los visitantes tus tipos personalizados

Para esto hay mil métodos, creo que uno muy deseable es que las recetas aparezcan en la página de inicio, vas a decirle al plugin que cuando WP vaya a buscar los artículos para la página de inicio se traiga también las recetas.

/**
 * Agrega el tipo personalizado Receta a la página inicial de WP
 */
function get_posts_y_recetas( $query ) {
   if ( is_home() AND $query->is_main_query() ) {
      $query->set( 'post_type', array( 'post', 'receta' ) );
   }

   return $query;
}

add_filter( 'pre_get_posts', 'get_posts_y_recetas' )

Carga ahora tu página de inicio y verás como tus posts y recetas aparecen publicados en ellas.

No te lo quedes dentro

¿Todo ha funcionado? Dímelo y te mando el cinturón amarillo desde ya.

¿Algo no te funciona? Házmelo saber en los comentario e intento ayudarte. Si ves algún fallo también te agradeceré mucho que me lo digas.

Si te ha gustado o te ha sido útil, te animo a compartirlo con otros a quien pueda interesarle, en las redes sociales, cantando el artículo mientras te duchas, gritándolo a los cuatro vientos o de cualquier otra manera que se te ocurra.

Y si quieres seguir haciendo cosas divertidas y útiles con WordPress acepto cualquier sugerencia para seguir escribiendo artículos.

Enlaces de referencia

4 comentarios en “Cómo crear tipos y campos personalizados en WordPress

  1. Pues después de dos horas buscando, éste es el primer post que encuentro sobre cómo crear campos personalizados sin usar un plugin. Lo que me parece raro es que no sea una de las posibilidades de generatewp.
    Ya te contaré… aunque es un poco difícil, pues no veo manera de sucribirse a tú newsletter.
    Un saludo

    1. Ufff, pues habrás tenido que bajar hasta las profundidades del infierno en los resultados de Google, porque de momento estoy en el abismo. Espero que te haya resultado de utilidad y a ver cuando puedo profundizar un poco más en el tema. De momento no tengo newsletter, no me atrevo a lanzarla porque ando muy escaso de tiempo y no quiero más compromisos, pero tarde o temprano… Si te parece bien me quedo con tu correo y te mando algo de manera informal de vez en cuando. Puedes escribirme a kungfupress@gmail.com o dejarme un comentario por aquí.

Deja un comentario

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