Custom Post Types

Una necesidad típica al construir sitios con WordPress es la de gestionar tipos de contenido diferentes de los que se incluyen por defecto. Normalmente esto se logra con la instalación de un plugin que agregue un nuevo post type. También es común encontrarse con themes que lo hagan, pero no es una práctica recomendable, salvo en casos muy específicos, ya que al cambiar el theme se perdería el nuevo post type.

Para mostrar cómo funciona este tipo de solución, vamos a crear un nuevo tipo de contenido que agregue a WordPress características de portfolio. Este tipo de contenido va permitir que un usuario cargue sus proyectos de trabajo, con una imagen asociada a cada uno de ellos, y también va a ofrecer opciones para cargar el nombre del cliente del trabajo, su fecha de finalización, y un link externo al proyecto terminado.

<?php
add_action( 'init', 'portfolio_create_project_post_type' );

function portfolio_create_project_post_type() {
    register_post_type( 'project', array(
            /**
             * @arg $labels
             * 
             * Define etiquetas de texto utilizadas por el post type.
             */
            'labels' => array(
                'name' => __( 'Projects', 'portfolio' ),          // Nombre (en plural) para este tipo de contenido.
                'singular_name' => __( 'Product' , 'portfolio' ), // Nombre (en singular) para este tipo de contenido.
            ),
            /**
             * @arg $supports
             * 
             * Define características soportadas por el post type.
             */
            'supports' => array(
                'title',     // Permite ingresar título
                'editor',    // Permite usar el editor de texto
                'author',    // Permite definir y modificar el autor
                'excerpt',   // Permite usar el campo de extracto de la entrada
                'thumbnail', // Permite ingresar imagen destacada
                'comments'   // Permite ingresar comentarios en la entrada
            ),
            'public' => true, // Hace accesibles las entradas desde el front-end.
            'has_archive' => true, // Hace accesible el sumario de entradas de este tipo.
        )
    );
}

Para crear un nuevo post type, vamos a usar la función register_post_type() al momento de ejecutarse el evento init. La función va a recibir dos parámetros: el primero es el nombre interno del post type, y el segundo es un array conteniendo su descripción. Dentro de este array podemos usar una serie de valores bastante completa, a partir de los cuales nuestro post type va a contar o no con ciertas características. Todos estos valores se describen con detalle en la documentación oficial de la función, y en este momento solamente vamos a usar aquellos que nos van a permitir que nuestros proyectos se vean de la manera más similar posible a un post. Vamos a definir el nombre del post type en plural y en singular, vamos a indicar cuáles de los campos predefinidos por WordPress necesitamos, y a indicar que las entradas que generemos puedan verse desde la parte pública de nuestro sitio, y también a través de una página de archivo.

De esta manera, cuando guardemos nuestro código, vamos a ver una nueva sección en el menú de administración: Projects. Podemos cargar un nuevo proyecto, con su correspondiente título, contenido, extracto, autor e imagen destacada, y una vez que lo guardemos WordPress nos va a dar un link para visualizarlo en el front-end. Si todo anduvo bien, deberíamos ver algo muy parecido a un post, quizás con algunas diferencias menores, dependiendo del theme que estemos usando.

Ahora bien, todavía no podemos ingresar el resto de los datos que dijimos que íbamos a necesitar: cliente, fecha de finalización y link externo. Para esto necesitamos agregar a nuestro plugin lo que aprendimos acerca de meta boxes. Vamos a implementar el código necesario para que puedan verse los campos que queremos agregar, y la funcionalidad necesaria para guardar esta información adicional.

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

function portfolio_register_meta_box() {
    add_meta_box(
        'portfolio-meta-box'
        __( 'Project Data', 'my-plugin' ),
        'portfolio_meta_box_callback',
        'project', // Indicamos que la meta box se muestre solo para projects.
    );
}

function portfolio_meta_box_callback( WP_Post $post ) {
    wp_nonce_field( '_project_data', '_project_data_nonce' );

    $client   = get_post_meta( $post->ID, '_project_client', true );
    $end_date = get_post_meta( $post->ID, '_project_end_date', true );
    $url      = get_post_meta( $post->ID, '_project_url', true );
?>
    <div class="portfolio-project-data-field">
        <label for="_project_client"><?php _( 'Client', 'portfolio' ); ?></label>
        <input type="text" name="_project_client"><?php echo $client; ?></input>
    </div>

    <div class="portfolio-project-data-field">
        <label for="_project_end_date"><?php _( 'End Date', 'portfolio' ); ?></label>
        <input type="text" name="_project_end_date"><?php echo $end_date; ?></input>
    </div>

    <div class="portfolio-project-data-field">
        <label for="_project_url"><?php _( 'Link', 'portfolio' ); ?></label>
        <input type="text" name="_project_url"><?php echo $url; ?></input>
    </div>
<?php
}

add_action( 'save_post', 'portfolio_save_project_data', 10, 2 );

function portfolio_save_project_data( $post_id, $post ) {
    // Si no se reciben los datos esperados, salir de la función.
    if ( ! isset( $_POST['_project_client'] ) || ! isset( $_POST['_project_end_date'] ) || ! isset( $_POST['_project_url'] ) ) {
        return;
    }

    // Si no se aprueba el chequeo de seguridad, salir de la función.
    if ( ! isset( $_POST['_project_data_nonce'] ) || ! wp_verify_nonce( $_POST['_project_data_nonce'], '_project_data' ) ) {
        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 formatos válidos para nuestros campos.
    $client   = sanitize_text_field( $_POST['_project_client'] );
    $end_date = sanitize_text_field( $_POST['_project_end_date'] );
    $client   = esc_url_raw( $_POST['_project_url'] );

    // Guardamos el nombre del cliente.
    if ( empty( $client ) ) {
        add_post_meta( $post_id, '_project_client', $client, true );
    } elseif ( $client != $_POST['_project_client'] ) {
        update_post_meta( $post_id, '_project_client', $client );
    }

    // Guardamos la fecha de finalización.
    if ( empty( $end_date ) ) {
        add_post_meta( $post_id, '_project_end_date', $end_date, true );
    } elseif ( $end_date != $_POST['_project_end_date'] ) {
        update_post_meta( $post_id, '_project_end_date', $end_date );
    }

    // Guardamos el link externo.
    if ( empty( $url ) ) {
        add_post_meta( $post_id, '_project_url', $url, true );
    } elseif ( $url != $_POST['_project_url'] ) {
        update_post_meta( $post_id, '_project_url', $url );
    }
}

Hasta el momento, ya sabemos cómo crear un nuevo post type, cómo adjuntarle datos adicionales y cómo guardar esa información. Sin embargo, todavía no podemos ver en el front-end los datos que cargamos en nuestra meta box. Una solución posible a eso es filtrar los datos del evento the_content, y mostrar nuestra información al final.

<?php
add_filter( 'the_content', 'portfolio_custom_content' );

function portfolio_custom_content( $content ) {
    $post_id = get_the_ID();

    if ( $client = esc_html( get_post_meta( $post_id, '_project_client', true ) ) ) {
        $content .= '<strong>' . __( 'Client:', 'portfolio' ) . '</strong> ' . $client . '<br />';
    }

    if ( $end_date = esc_html( get_post_meta( $post_id, '_project_end_date', true ) ) ) {
        $content .= '<strong>' . __( 'End Date:', 'portfolio' ) . '</strong> ' . $end_date . '<br />';
    }

    if ( $url = esc_url( get_post_meta( $post_id, '_project_url', true ) ) ) {
        $content .= '<strong>' . __( 'Link:', 'portfolio' ) . '</strong> ' . $url . '<br />';
    }

    return $content;
}

results matching ""

    No results matching ""