Custom Fields y Meta Boxes

La manera más rápida de agregar valores extra a los contenidos de un sitio es por medio del uso de campos personalizados, o Custom Fields. Estos campos personalizados se hacen visibles en la pantalla de creación o edición de un contenido cuando se chequea el campo correspondiente en Opciones de Pantalla. Esto va a hacer que WordPress muestre un menú en el que se puede agregar un nuevo campo con un nombre a elección y su correspondiente valor.

Puede obtenerse e imprimirse en pantalla ese valor desde un plugin de esta manera:

<?php
add_filter( 'the_title', 'apply_title_color' );

function apply_title_color( $title ) {
    $color = get_post_meta( get_the_ID(), 'title_color', true );

    if ( $color ) {
        $title = '<span style="color: ' . $color . '">' . $title . '</span>';
    }

    return $title;
}

O mostrar todos los valores de campos personalizados previamente cargados de esta forma:

<?php
add_filter( 'the_content', 'show_post_meta' );
function show_post_meta( $content ) {
    ob_start();

    the_meta();
    $content .= ob_get_contents();

    ob_end_clean();

    return $content;
}

La ventaja de esta forma de gestionar campos personalizados es que es rápida. Hace falta muy poco código para mostrar nuevos datos. Es muy útil para salir de un apuro o con fines de testing, pero para desarrollo de plugins dista bastante de ser óptima. En primer lugar, porque los campos tienen que ser creados manualmente, y se requiere que el usuario conozca el nombre de estos campos para poder ingresar información. Esto no es una muy buena práctica, porque parte de la lógica de la aplicación se manejaría como contenido, y no como código.

Por otra parte, este método también es limitante en cuanto a cómo se carga la información: no se puede modificar el formato de los campos personalizados. Su formato por defecto puede ser muy útil para ingresar texto, pero si se necesita manejar otro tipo de información, como un array, o elegir entre una cantidad limitada de opciones, los campos personalizados no alcanzan.

La solución es crear meta boxes. Las meta boxes son, básicamente, campos personalizados con esteroides. A la hora de guardar y obtener información funcionan casi de la misma manera que los campos personalizados, pero brindan control completo sobre el HTML que generan, por lo cual se puede crear cualquier tipo de campo para que los usuarios ingresen datos, en lugar de limitarnos a un input de texto. Además, manejan el nombre del campo internamente, por lo cual no es necesario que el usuario lo conozca a la hora de ingresar datos. También ofrecen control absoluto acerca de cómo se guarda esa información, por lo cual, si se necesita hacer algún tipo de validación de datos o correr algún tipo de proceso sobre la información ingresada antes de guardarla, puede hacerse sin problema.

Agregar meta boxes es muy sencillo, y para hacerlo es necesario asignar una función al evento add_meta_boxes, el cual corresponde al momento en el que WordPress registra este tipo de información.

<?php
add_action( 'add_meta_boxes', 'register_meta_box' );

function register_meta_box() {
    add_meta_box(
        $id,       // Atributo "id" impreso en el contenedor HTML. String.
        $title,    // Título de meta box. String.
        $callback, // Función que imprime nuestro HTML. String o función anónima.
        $screen    // Opcional. Vista o vistas donde va a aparecer la meta box. Nombres de post types u objeto WP_Screen.
    );
}

Teniendo en cuenta estos parámetros, la función se vería similar a esta:

<?php
add_action( 'add_meta_boxes', 'register_meta_box' );

function register_meta_box() {
    add_meta_box(
        'my-meta-box'
        __( 'My meta box', 'my-plugin' ),
        'my_metabox_callback',
        'post' // También son válidos 'page', array( 'post', 'page' ) y current_screen().
    );
}

function my_metabox_callback() {
    echo 'Hello world!'
}

Aunque con este código todavía no es posible cargar nueva información, ya puede verse una nueva meta box en las pantallas de edición y creación de posts. La función add_meta_box() también acepta otros tres parámetros opcionales más: $context, para definir en qué sección de la pantalla va a aparecer la meta box; $priority, para indicar qué prioridad tiene dentro del contexto en el que se la registra; y $callback_args, para enviarle información adicional a nuestro callback dentro de un array.

El siguiente paso es imprimir, a través del callback, algún tipo de información que un usuario pueda cargar. Para eso simplemente puede crearse un campo de formulario dentro de la función e imprimirlo.

Con este código puede verse un campo de tipo select dentro de la meta box:

<?php
add_action( 'add_meta_boxes', 'register_meta_box' );

function register_meta_box() {
    add_meta_box(
        'my-meta-box'
        __( 'My meta box', 'my-plugin' ),
        'my_metabox_callback',
        'post', // También son válidos 'page', array( 'post', 'page' ) y current_screen().
    );
}

function my_metabox_callback() { ?>
    <select name="_my_selectable_option">
        <option value="value-1"><?php _e( 'Value 1', 'my-plugin' ); ?></option>
        <option value="value-2"><?php _e( 'Value 2', 'my-plugin' ); ?></option>
    </select>
<?php
}

Con lo logrado hasta ahora, un usuario puede seleccionar información, pero aún no puede guardarla. Para eso vamos a necesitar una nueva acción en el evento save_post, donde se chequee y se guarde la información que estamos enviando.

<?php
add_action( 'add_meta_boxes', 'register_meta_box' );
function register_meta_box() {
    add_meta_box(
        'my-meta-box'
        __( 'My meta box', 'my-plugin' ),
        'my_metabox_callback',
        'post' // También son válidos 'page', array( 'post', 'page' ) y current_screen().
    );
}

function my_metabox_callback() { ?>
    <select name="_my_selectable_option">
        <option value="value-1"><?php _e( 'Value 1', 'my-plugin' ); ?></option>
        <option value="value-2"><?php _e( 'Value 2', 'my-plugin' ); ?></option>
    </select>
<?php
}

add_action( 'save_post', 'save_my_meta_box_data' );
function save_my_meta_box_data( $post_id ) {
    $data = get_post_meta( $post_id, '_my_selectable_option', true );

    if ( empty( $data ) ) {
        add_post_meta( $post_id, '_my_selectable_option', $_POST['_my_selectable_option'], true );
    } elseif ( $data != $_POST['_my_selectable_option'] ) {
        update_post_meta( $post_id, '_my_selectable_option', $_POST['_my_selectable_option'] );
    }
}

Este código sirve para guardar la información ingresada en la meta box al guardar un post. Lo que hace es chequear si ya existen datos para el post actual con el nombre del campo creado. Si no existen, los ingresa en la base de datos; si existen, y además son distintos de los que estamos enviando al guardar, los actualiza. Sin embargo, no se está haciendo ningún chequeo de seguridad, por lo cual un usuario malicioso podría ingresar cualquier tipo de información en ese campo, provocando problemas bastante graves. Es por eso que necesitamos ser más específicos en nuestros chequeos.

<?php
/**
 * Necesitamos chequear:
 *
 * - Que la información que hace falta actualizar se esté enviando.
 * - Que la actualización se esté haciendo en el momento y lugar correctos.
 * - Que el usuario tenga permisos para modificar la información.
 * - Que la información a actualizar sea válida.
 */

add_action( 'add_meta_boxes', 'register_meta_box' );
function register_meta_box() {
    add_meta_box(
        'my-meta-box'
        __( 'My meta box', 'my-plugin' ),
        'my_metabox_callback',
        'post' // También son válidos 'page', array( 'post', 'page' ) y current_screen().
    );
}

function my_metabox_callback() {
    wp_nonce_field( '_my_selectable_option', '_my_selectable_option_nonce' );
?>
    <select name="_my_selectable_option">
        <option value="value-1"><?php _e( 'Value 1', 'my-plugin' ); ?></option>
        <option value="value-2"><?php _e( 'Value 2', 'my-plugin' ); ?></option>
    </select>
<?php
}

add_action( 'save_post', 'save_my_meta_box_data', 10, 2 );
function save_my_meta_box_data( $post_id, $post ) {
    // Si no se reciben datos, salir de la función.
    if ( ! isset( $_POST['_my_selectable_option'] ) ) {
        return;
    }

    // Si no se aprueba el chequeo de seguridad, salir de la función.
    if ( ! isset( $_POST['_my_selectable_option_nonce'] ) || ! wp_verify_nonce( $_POST['_my_selectable_option_nonce'], '_my_selectable_option' ) ) {
        return;
    }

    $post_type = get_post_type_object( $post->post_type );

    // Si el usuario actual no tiene permisos para modificar el post, salir de la función.
    if ( ! current_user_can( $post_type->cap->edit_post, $post_id ) ) {
        return;
    }

    // Convertimos los datos ingresados a un formato válido para nuestro campo.
    $valid_data = esc_html( $_POST['_my_selectable_option'] );

    $data = get_post_meta( $post_id, '_my_selectable_option', true );

    if ( empty( $data ) ) {
        add_post_meta( $post_id, '_my_selectable_option', $valid_data, true );
    } elseif ( $data != $_POST['_my_selectable_option'] ) {
        update_post_meta( $post_id, '_my_selectable_option', $valid_data );
    }
}

En el primer chequeo, en caso de que no se reciba la información esperada, simplemente se sale de la función sin guardar. El segundo chequeo es algo más complejo, pero sirve para introducir un concepto muy importante en cuestión de seguridad: los nonces.

Un nonce es, básicamente, un tipo de dato generado por WordPress que consiste en un valor numérico variable, y que permite chequear que un proceso dado se esté ejecutando en el momento y desde el lugar adecuados. Está pensado para cuando se necesita ejecutar operaciones delicadas, que requieren un cierto nivel de seguridad, como el almacenamiento de información en la base de datos.

Para poder chequear que un nonce sea válido antes de guardar datos, primero se lo debe crear. Es por eso que dentro del callback que genera el HTML de nuestra meta box va a usarse la función wp_nonce_field() con dos parámetros: el primero es una palabra clave que permite identificar el contexto en el que se genera el nonce, y el segundo es el nombre del dato que va a contener el valor numérico del nonce y va a llegar por medio de la variable global $_POST cuando se guarde la entrada.

Al chequear un nonce debe confirmarse que el dato que lo contiene está creado, lo cual hacemos con isset(). También se necesita chequear con wp_verify_nonce() que el dato numérico corresponda al contexto definido. Si alguna de estas dos cosas no pasa, se sale de la función sin guardar información. Si se pasan ambos chequeos, se continúa.

Por último, debe confirmarse que los datos que se van a ingresar tengan el formato correcto. Por ejemplo, si lo que se desea guardar en la base de datos es un array, pero recibimos un string, se debe lo debe convertir en un array; si se espera texto plano pero se recibe HTML, se debe convertir a texto plano. Este proceso se llama sanitización, y permite asegurar que un usuario malicioso no va a poder ingresar algún tipo de información que dañe la base de datos. Además, garantiza que los datos introducidos en el sistema siempre van a ser los esperables.

En el ejemplo se usa la función esc_html() para asegurarse de que cualquier texto que pueda ser ingresado como HTML se convierta en texto plano. Hay una cantidad bastante grande de funciones de sanitización provistas por WordPress, las cuales se pueden chequear en la documentación oficial. Algunas de esas funciones son más recomendables para mostrar datos (lo cual se llama escape), y otras son preferibles a la hora de guardarlos (lo cual se llama validación).

results matching ""

    No results matching ""