Usar taxonomías en el campo select de un formulario

Cuando desarrolles un formulario en WordPress te vas a encontrar casi siempre con la necesidad de mostrar al usuario una lista de opciones para seleccionar una de ellas, el típico campo «select». A veces, por acabar antes, pones las opciones grabadas «a fuego» en tu código, pero cuando más adelante necesites introducir o quitar opciones, tendrás que editar el código. La cosa se complica si cuando surja la necesidad no estás tú ahí para hacerlo.

Por ello una buena práctica es sacar siempre estas opciones del código y llevarlas a la base de datos. Podrías crear una tabla para ello, pero luego tendrías que crear un listado y un formulario para revisar, agregar o modificar elementos.

Si quieres ahorrarte este trabajo te recomiendo usar las taxonomías de WordPress. En este tutorial aprenderás a usar taxonomías en el campo select de un formulario y a guardarla con el resto de datos. En este ejemplo concreto cada envío del formulario lo almacenarás en un Custom Post Type (CPT).

Creando la base del plugin y el CPT

Lo primero que vas a hacer es crear la estructura básica del plugin y agregar un CPT de la forma más breve posible.

Crea una carpeta con el nombre del plugin y un fichero PHP dentro con el mismo nombre de la carpeta. Contendrá un par de constantes con información del plugin y la llamada al fichero plugin-init.php donde luego definirás el tipo de entrada personalizada (CPT) y la taxonomía personalizada.

Como siempre, tienes el código completo del plugin en GitHub, pero recuerda que si estás siguiendo este tutorial con fines de aprendizaje te recomiendo teclear todo el código de tu puño y letra.

<?php
/**
 * Plugin Name:  KFP FormTaxon
 * Plugin URI:   https://github.com/kungfupress/kfp-formtaxon
 * Description:  Ejemplo de utilización de una categoría personalizada desde un formulario
 * Version:      0.1.0
 * Author:       Juanan Ruiz
 * Author URI:   https://kungfupress.com/
 * PHP Version:  5.6
 *
 * @package  kfp_ftx
 */

defined( 'ABSPATH' ) || die();

// Constantes que afectan a todos los ficheros del plugin.
define( 'KFP_FTX_DIR', plugin_dir_path( __FILE__ ) );
define( 'KFP_FTX_URL', plugin_dir_url( __FILE__ ) );
define( 'KFP_FTX_VERSION', '0.1.0' );

// Crea CPT y taxonomia.
require_once KFP_FTX_DIR . 'include/plugin-init.php';

Creación del CPT

Crea una carpeta include y dentro de ella el archivo plugin-init.php. En este archivo vas a crear el CPT y la taxonomía.

Vas a utilizar el hook add_action('init') de manera que el CPT esté definido antes de ejecutar otras acciones del plugin. Para definir dicho CPT usarás la función register_post_type() que recibe como parámetros el slug o nombre interno del CPT y un array de argumentos donde se pueden personalizar muchos aspectos del mismo, aunque en este ejemplo será lo mínimo para ir al grano. Tienes otro artículo más extenso sobre cómo crear tipos y campos personalizados

<?php
/**
 * File: kfp-formtaxon/include/plugin-init.php
 *
 * @package kfp_formtaxon
 */

defined( 'ABSPATH' ) || die();

add_action( 'init', 'kfp_cpt_taller', 10 );
/**
 * Crea el CPT Taller con lo mínimo que se despacha en CPTs
 * @return void
 */
function kfp_cpt_taller() {
	$args = array(
		'public' => true,
		'label'  => 'Taller',
	);
	register_post_type( 'kfp-taller', $args );
}

Cómo crear una taxonomía personalizada

Agrega el código para definir la taxonomía y algunos ejemplos predeterminados de esta. Para ello vas a usar de nuevo el hook add_action('init') y la función register_taxonomy() que recibe tres parámetros: el slug de la taxonomía, un array con los tipos de entrada que pueden usar esta categoría y otro array con el resto de propiedades, de nuevo al mínimo.

En este ejemplo el CPT representa una actividad formativa (taller) y la taxonomía el lugar donde se imparte.

add_action( 'init', 'kfp_taxonomy_lugares', 0 );
/**
 * Registra la taxonomía con lo mínimo indispensable
 * @return void
 */
function kfp_taxonomy_lugares() {
	$args = array(
		'label'             => 'Lugar',
		'hierarchical'      => true,
		'show_admin_column' => true,
	);
	register_taxonomy( 'kfp-lugar', array( 'kfp-taller' ), $args );
}

add_action( 'init', 'kfp_lugares_add', 1 );
/**
 * Agrega algunos lugares de ejemplo por defecto
 * @return void
 */
function kfp_lugares_add() {
	$lugares = array(
		'Escuela de Ingenieros Informáticos',
		'Facultad de Derecho',
		'Facultad de Bellas Artes',
		'Facultad de Medicina',
		'Rectorado',
	);
	foreach ( $lugares as $lugar ) {
		wp_insert_term( $lugar, 'kfp-lugar' );
	}
}

Creación del formulario

Ahora vas a crear el formulario que te permitirá crear un nuevo taller desde el frontend. Para mostrarlo en la parte pública o frontend de tu web, podrías crear una plantilla específica o aprovechar algún elemento de la plantilla como la cabecera, el pié o incluso un widget. Para este ejemplo vas a asociar el formulario a un shortcode, esto te permitirá incluirlo en cualquier página o entrada que desees, incluso en un widget en el que podrías colocar el shortcode.

Si prefieres tener el formulario en el backend podrías crear un panel de administración allí y reutilizar el código que verás aquí con algunas adaptaciones.

Formulario con taxonomía en campo select

Así que, crea un nuevo fichero llamado form_taller_shortcode.php dentro de la carpeta include. Recuerda que necesitas mostrar la lista de lugares en el formulario, así que lo primero que harás será recuperar todos los elementos de la taxonomía utilizando la función get_terms() de WordPress.

Luego «pintas» el formulario, que se va a procesar con el fichero admin-post.php de WordPress, tal como se indica en el action del formulario. Un campo oculto del formulario llamado también action permitirá a WordPress saber que formulario es el que estás grabando por lo que debes usar un identificador único, yo he escogido kfp-ftx-taller.

El formulario se completa con un campo nonce de seguridad, el nombre y la descripción del nuevo taller y por supuesto la lista desplegable para seleccionar el lugar donde se va a celebrar el taller.

<?php
/**
 * File: kfp-formtaxon/include/form-taller-shortcode.php
 *
 * @package kfp_ftx
 */

defined( 'ABSPATH' ) || die();

add_shortcode( 'kfp_ftx_crear_taller', 'kfp_ftx_crear_taller' );
/**
 * Implementa formulario para crear un nuevo taller.
 *
 * @return string
 */
function kfp_ftx_crear_taller() {
	// Trae los lugares existentes en la base de datos a la vatiable $lugares.
	// Esta variable recibirá un array de objetos de tipo taxonomy.
	$lugares = get_terms(
		'kfp-lugar',
		array(
			'orderby'    => 'term_id',
			'hide_empty' => 0,
		)
	);
	ob_start();
	?>
	<form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post">
		<?php wp_nonce_field( 'kfp-ftx-taller', 'kfp-ftx-taller-nonce' ); ?>
		<input type="hidden" name="action" value="kfp-ftx-taller">
		<div class="form-input">
			<label for="nombre">Taller</label>
			<input type="text" name="nombre" id="nombre" required>
		</div>
		<div class="form-input">
			<label for="id_lugar">Lugar</label>
			<select name="id_lugar" required>
				<option value="">Selecciona el lugar</option>
				<?php
				foreach ( $lugares as $lugar ) {
					echo(
						'<option value="' . esc_attr( $lugar->term_id ) . '">'
						. esc_attr( $lugar->name ) . '</option>'
					);
				}
				?>
			</select>
		</div>
		<div class="form-input">
			<label for="descripcion">Descripción</label>
			<textarea name="descripcion" id="descripcion"></textarea>
		</div>
		<div class="form-input">
			<input type="submit" value="Enviar">
		</div>
	</form>
	<?php
	return ob_get_clean();
}

Crea en tu web una página o entrada donde colocarás el shortcode [kfp_ftx_crear_taller]. Comprueba que el formulario aparece dentro de la entrada y que el campo Lugar contiene los elementos de ejemplo que pusiste al definir la taxonomía.

Grabar el formulario y la taxonomía en la base de datos

Para grabar los datos del formulario usarás un hook dinámico (parte del nombre lo inventas tú) que será procesado por el fichero wp-admin/admin-post.php. De momento el script procesará tanto el envío de usuarios autenticados como anónimos, por lo que utilizarás dos hooks: admin_post__{$action} y admin_post_nopriv_{$action}. Recuerda que el valor de {$action} lo has definido en un campo oculto del formulario.

Esquema de grabación de los datos de un formulario utilizando admin-post.php

Crea un nuevo fichero include/form-talller-grabar.php Agrega en él los hooks que acabo de explicar.

Define la función kfp_ftx_graba_taller() donde una vez comprobados que los campos requeridos vienen rellenos y que el nonce es correcto podrás grabar el CPT de tipo taller utilizando la función wp_insert_post()

<?php
/**
 * File: kfp-formtaxon/include/form-taller-grabar.php
 *
 * @package kfp_ftx
 */
defined( 'ABSPATH' ) || die();
// Agrega los action hooks para grabar el formulario (el primero para usuarios
// logeados y el otro para el resto)
// Lo que viene tras admin_post_ y admin_post_nopriv_ tiene que coincidir con
// el value del campo input con name "action" del formulario enviado.
add_action( 'admin_post_kfp-ftx-taller', 'kfp_ftx_graba_taller' );
add_action( 'admin_post_nopriv_kfp-ftx-taller', 'kfp_ftx_graba_taller' );
/**
 * Graba los datos enviados por el formulario como un nuevo CPT kfp-taller
 *
 * @return void
 */
function kfp_ftx_graba_taller() {
	// Comprueba campos requeridos y nonce.
	if ( isset( $_POST['nombre'] )
	&amp;&amp; isset( $_POST['id_lugar'] )
	&amp;&amp; isset( $_POST['kfp-ftx-taller-nonce'] )
	&amp;&amp; wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['kfp-ftx-taller-nonce'] ) ), 'kfp-ftx-taller' )
	) {
		$nombre      = sanitize_text_field( wp_unslash( $_POST['nombre'] ) );
		$descripcion = sanitize_text_field( wp_unslash( $_POST['descripcion'] ) );
		$id_lugar    = (int) $_POST['id_lugar'];
		$args = array(
			'post_title' => $nombre,
			'post_content' => $descripcion,
			'post_type' => 'kfp-taller',
			'post_status' => 'draft',
			'comment_status' => 'closed',
			'ping_status' => 'closed'
		);
		// Esta variable $post_id contiene el ID del nuevo registro
		// Viene de perlas para grabar los metadatos
		$post_id = wp_insert_post($args);
	}
}

Comprueba que todo funciona y el nuevo CPT se graba. Al enviar el formulario obtendrás una pantalla en blanco, pero puedes comprobar que se ha creado un nuevo taller desde el escritorio. Luego arreglarás lo de la pantalla en blanca, primero tienes que asociar la taxonomía lugar con el CPT taller .

Cómo grabar una taxonomía en un post

En el paso anterior, al grabar el nuevo post de tipo taller, capturabas su indentificador (ID) en la variable $post_id, que era lo que te devolvía la función wp_insert_post().

Ahora vas a usar la función wp_set_object_terms() que recibe el ID del post como primer parámetro. Luego necesitas el ID de la taxonomía que has asignado desde el formulario (lo has capturado en la variable $id_lugar) y por último el tipo de taxonomía, en este caso 'kfp-lugar' Quedando el final del código así:

$post_id = wp_insert_post($args);
$term_taxonomy_ids = wp_set_object_terms( $post_id, $id_lugar, 'kfp-lugar' );

Sólo tendrías, por tanto, que añadir la última línea, volver a grabar un nuevo taller desde el formulario y comprobar desde el escritorio que la taxonomía se ha grabado con el taller.

Para terminar tendrás que salir de esa pantalla en blanco que aparece tras enviar el formulario. No es un error, es normal, precisamente estás usando admin-post.php porque es mucho más rápido que cargar el sitio completo, el tema y los contenidos actuales. Con admin-post.php procesas el formulario con un WordPress que carga lo mínimo y luego rediriges a donde quieras. En este caso al mismo formulario con un mensaje de éxito o error, pero podría enviar a una página con un listado con todos los talleres propuestos hasta el momento o de todos los talleres propuestos por este usuario.

Aquí tienes el código completo, incluyendo redirecciones y mensajes, del fichero include/form-taller-grabar.php

<?php
/**
 * File: kfp-formtaxon/include/form-taller-grabar.php
 *
 * @package kfp_ftx
 */

defined( 'ABSPATH' ) || die();
// Agrega los action hooks para grabar el formulario (el primero para usuarios
// logeados y el otro para el resto)
// Lo que viene tras admin_post_ y admin_post_nopriv_ tiene que coincidir con
// el valor del campo input con nombre "action" del formulario enviado.
add_action( 'admin_post_kfp-ftx-taller', 'kfp_ftx_graba_taller' );
add_action( 'admin_post_nopriv_kfp-ftx-taller', 'kfp_ftx_graba_taller' );

/**
 * Graba los datos enviados por el formulario como un nuevo CPT kfp-taller
 *
 * @return void
 */
function kfp_ftx_graba_taller() {
	// Si viene en $_POST aprovecha uno de los campos que crea wp_nonce para volver al form.
	$url_origen = home_url( '/' );
	if ( ! empty( $_POST['_wp_http_referer'] ) ) {
		$url_origen = esc_url_raw( wp_unslash( $_POST['_wp_http_referer'] ) );
	}
	// Comprueba campos requeridos y nonce.
	if ( isset( $_POST['nombre'] )
	&amp;&amp; isset( $_POST['id_lugar'] )
	&amp;&amp; isset( $_POST['kfp-ftx-taller-nonce'] )
	&amp;&amp; wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['kfp-ftx-taller-nonce'] ) ), 'kfp-ftx-taller' )
	) {
		$nombre      = sanitize_text_field( wp_unslash( $_POST['nombre'] ) );
		$descripcion = sanitize_text_field( wp_unslash( $_POST['descripcion'] ) );
		$id_lugar    = (int) $_POST['id_lugar'];
		$args = array(
			'post_title' => $nombre,
			'post_content' => $descripcion,
			'post_type' => 'kfp-taller',
			'post_status' => 'draft',
			'comment_status' => 'closed',
			'ping_status' => 'closed'
		);
		// Esta variable $post_id contiene el ID del nuevo registro
		// Viene de perlas para grabar la taxonomía
		$post_id = wp_insert_post($args);
		$term_taxonomy_ids = wp_set_object_terms( $post_id, $id_lugar, 'kfp-lugar' );
		$query_arg = array( 'kfp-ftx-resultado' => 'error' );
		wp_redirect( esc_url_raw( add_query_arg( $query_arg , $url_origen ) ) );
		exit();
	}
	$query_arg = array( 'kfp-ftx-resultado' => 'error' );
	wp_redirect( esc_url_raw( add_query_arg( $query_arg , $url_origen ) ) );
	exit();
}

Verás que ahora el formulario redirige a la propia página o entrada del formulario y en la barra de direcciones de tu navegador aparece el resultado (‘success’ o ‘error’) Ahora tienes que hacer que aparezca dentro de la página.

Para ello coloca estos dos «if» justo antes de «pintar» el formulario en el fichero include/form-taller-shortcode.php

ob_start();
	if ( filter_input( INPUT_GET, 'kfp-ftx-resultado', FILTER_SANITIZE_STRING ) === 'success' ) {
		echo '<h4>Se ha grabado el taller correctamente</h4>';
	}
	if ( filter_input( INPUT_GET, 'kfp-ftx-resultado', FILTER_SANITIZE_STRING ) === 'error' ) {
		echo '<h4>Se ha producido un error al grabar el taller</h4>';
	}
	?>
	<form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post">

Para terminar te comento las posibles formas de administrar tus taxonomías personalizadas.

Cómo administrar tu taxonomía personalizada

Si estás trabajando con entradas, páginas o entradas personalizadas (CPTs) el menú de administración de la taxonomía lo encontraras asociado al menú de administración de los tipos de entrada a los que la taxonomía está asociada.

Panel para administrar taxonomía personalizada desde el escritorio de WordPress

En la imagen de arriba ves la taxonomía «Lugar» asociada al Custom Post Type «Taller», desde el menú de este CPT puedes acceder al listado, creación, edición y borrado de elementos de esta taxonomía.

Si estás trabajando con una tabla propia (algo que siempre debe quedar cómo última opción) podrías asociar la taxonomía a las entradas (a los posts vamos) y desde allí podrás administrarla igualmente. Pero quizás podría quedar más elegante (y más usable) si desde el propio formulario pones un enlace o un botón al panel de edición de la taxonomía. En el caso de este ejemplo sería algo como:

<a href="wp-admin/edit-tags.php?taxonomy=provincias">Editar provincias</a>

Realmente esta última opción también la puedes utilizar aunque estés usando un Custom Post Type, siempre que el formulario esté en el escritorio o backend (para el frontend sería otro cantar y quizás no tendría sentido que cualquier usuario pudiera editar una taxonomía).

Conclusiones

Utilizar taxonomías es la manera óptima de ofrecer a tus usuarios una lista de opciones a la hora de rellenar un formulario, en algunos casos incluso podrías permitir a los propios usuarios agregar nuevas opciones si fuera necesario. En cualquier caso el uso de taxonomías proporciona a los administradores del sitio las herramientas necesarias para administrarlas, herramientas que ya vienen incluidas en WordPress. Cuando implementes lo aquí aprendido en un par de ocasiones te preguntarás como habías podido vivir hasta ahora sin utilizar taxonomías personalizadas.

Espero que te haya gustado el tutorial y le puedas sacar provecho, te recuerdo que tienes el código completo del plugin en GitHub.

Referencias

Deja un comentario

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