WordPress es una plataforma de gestión de contenido extremadamente versátil que permite a los usuarios crear una amplia variedad de sitios web, desde blogs personales hasta tiendas en línea complejas. Uno de los puntos fuertes de WordPress es su capacidad para personalizarse a través de temas y plugins.
En este tutorial, nos centraremos en la creación de un tema de WordPress desde cero. Utilizaremos un enfoque básico pero funcional para mostrar libros. Utilizaremos un Custom Post Type para representar los libros en nuestro sitio web y crearemos plantillas específicas para mostrarlos como queremos.
Definir la plantilla
Primero creamos nuestro style.css que es básico para que wordpress nos entienda, en nuestro caso solo lo usamos para enlazar al css que realmente vamos a usar:
/* Theme Name: Trecebook Theme URI: https://13node.com/informatica/wordpress/tutorial-para-crear-theme-desde-cero-con-cpt/ Description: Tema básico para mostrar libros en WordPress. Author: 13Node Author URI: https://13node.com Version: 1.0 License: GNU General Public License v2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Text Domain: trecebook */ @import url("css/init.css"); @import url("css/main.css"); @import url("css/media.css");
Crear el Custom Post Type ‘Libro’ y la Taxonomía ‘Género’
Este es el segundo paso, crearemos nuestras funciones en un nuevo archivo llamado functions.php
y como todo archivo functions.php empezamos con el encabezado del tema:
<?php /* Theme Name: Trecebook Theme URI: https://13node.com/informatica/wordpress/tutorial-para-crear-theme-desde-cero-con-cpt/ Description: Tema básico para mostrar libros en WordPress. Author: 13Node Author URI: https://13node.com Version: 1.0 License: GNU General Public License v2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Text Domain: trecebook */
Ahora añadiremos el código de nuestro CPT para poder añadir nuestros libros desde el backoffice de wordpress:
function trecebook_cpt() { $labels = array( 'name' => _x('Libros', 'post type general name', 'trecetextdomain'), 'singular_name' => _x('Libro', 'post type singular name', 'trecetextdomain'), 'menu_name' => _x('Libros', 'admin menu', 'trecetextdomain'), 'name_admin_bar' => _x('Libro', 'add new on admin bar', 'trecetextdomain'), 'add_new' => _x('Agregar Nuevo', 'book', 'trecetextdomain'), 'add_new_item' => __('Agregar Nuevo Libro', 'trecetextdomain'), 'new_item' => __('Nuevo Libro', 'trecetextdomain'), 'edit_item' => __('Editar Libro', 'trecetextdomain'), 'view_item' => __('Ver Libro', 'trecetextdomain'), 'all_items' => __('Todos los Libros', 'trecetextdomain'), 'search_items' => __('Buscar Libros', 'trecetextdomain'), 'parent_item_colon' => __('Libro Padre:', 'trecetextdomain'), 'not_found' => __('No se encontraron libros.', 'trecetextdomain'), 'not_found_in_trash' => __('No se encontraron libros en la papelera.', 'trecetextdomain') ); $args = array( 'labels' => $labels, 'public' => true, 'publicly_queryable' => true, 'show_ui' => true, 'show_in_menu' => true, 'query_var' => true, 'rewrite' => array('slug' => 'libro'), 'capability_type' => 'post', 'has_archive' => true, 'hierarchical' => false, 'menu_position' => null, 'supports' => array('title', 'editor', 'thumbnail') ); register_post_type('libro', $args); add_action('add_meta_boxes', 'add_libro_meta_boxes'); add_action('save_post', 'save_libro_meta_data'); }
A continuación creamos las meta boxes personalizadas que necesitamos, para la portada y el título usaremos la imagen destacada y el propio título de wordpress.
function add_libro_meta_boxes() { add_meta_box( 'libro_meta_box', 'Detalles del Libro', 'render_libro_meta_box', 'libro', 'normal', 'default' ); } function render_libro_meta_box($post) { wp_nonce_field('save_libro_meta_data', 'libro_meta_box_nonce'); $autores = get_post_meta($post->ID, 'autores', true); $anio_publicacion = get_post_meta($post->ID, 'anio_publicacion', true); echo '<label for="autores">Autores</label>'; echo '<input type="text" id="autores" name="autores" value="' . esc_attr($autores) . '" size="25" />'; echo '<br>'; echo '<label for="anio_publicacion">Año de publicación</label>'; echo '<input type="text" id="anio_publicacion" name="anio_publicacion" value="' . esc_attr($anio_publicacion) . '" size="25" />'; } function save_libro_meta_data($post_id) { if (!isset($_POST['libro_meta_box_nonce'])) { return; } if (!wp_verify_nonce($_POST['libro_meta_box_nonce'], 'save_libro_meta_data')) { return; } if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return; } if (!current_user_can('edit_post', $post_id)) { return; } $autores = sanitize_text_field($_POST['autores']); $anio_publicacion = sanitize_text_field($_POST['anio_publicacion']); update_post_meta($post_id, 'autores', $autores); update_post_meta($post_id, 'anio_publicacion', $anio_publicacion); } add_action('init', 'trecebook_cpt');
Queremos que en vez de categorías, se categorice (valga la redundancia) en Géneros:
function trecebook_taxonomia_genero() { $labels = array( 'name' => _x('Géneros', 'taxonomy general name', 'trecetextdomain'), 'singular_name' => _x('Género', 'taxonomy singular name', 'trecetextdomain'), 'search_items' => __('Buscar Géneros', 'trecetextdomain'), 'all_items' => __('Todos los Géneros', 'trecetextdomain'), 'parent_item' => __('Género Padre', 'trecetextdomain'), 'parent_item_colon' => __('Género Padre:', 'trecetextdomain'), 'edit_item' => __('Editar Género', 'trecetextdomain'), 'update_item' => __('Actualizar Género', 'trecetextdomain'), 'add_new_item' => __('Agregar Nuevo Género', 'trecetextdomain'), 'new_item_name' => __('Nuevo Nombre de Género', 'trecetextdomain'), 'menu_name' => __('Género', 'trecetextdomain'), ); $args = array( 'hierarchical' => true, 'labels' => $labels, 'show_ui' => true, 'show_admin_column' => true, 'query_var' => true, 'rewrite' => array('slug' => 'genero'), ); register_taxonomy('genero', array('libro'), $args); } add_action('init', 'trecebook_taxonomia_genero');
Y terminamos con nuestra query para mostrar solo los CPT de Libros.
function trece_libros_query() { $args = array( 'post_type' => 'libro', 'posts_per_page' => -1, ); return new WP_Query($args); }
Plantilla Base del Tema
Primero, necesitamos crear la estructura básica de nuestro tema de WordPress. Aquí está el código para nuestra plantilla base (index.php
) con nuestra query solo para libros:
<?php get_header(); ?> <section class="content content-main"> <div class="content-video col8-12 col12-12-m"> <iframe width="560" height="315" src="https://www.youtube.com/embed/Yb_zOs6v87U?si=i-7IFgWt3smv-oGr" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> </div> <div class="content-txt col8-12 col10-12-m col12-12-s"> <h1>Lorem ipsum dolor, sit amet consectetur adipisicing elit.</h1> </div> </section> <section class="content content-grid"> <?php $libros_query = trece_libros_query(); if ($libros_query->have_posts()) : while ($libros_query->have_posts()) : $libros_query->the_post(); ?> <div class="content-grid-item col3-12 col4-12-m col6-12-s"> <a href="<?php the_permalink(); ?>"> <div class="content-grid-item-img"><?php the_post_thumbnail('thumbnail'); ?></div> <div class="content-grid-item-title"> <h2><?php the_title(); ?></h2> </div> </a> </div> <?php endwhile; wp_reset_postdata(); else : echo '<p class="text-center">No se encontraron libros.</p>'; endif; ?> </section> <?php get_footer(); ?>
Obviamente necesitaremos el header.php
:
<!DOCTYPE html> <html <?php language_attributes(); ?>> <head> <meta charset="<?php bloginfo('charset'); ?>"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title><?php wp_title(); ?></title> <?php wp_head(); ?> <link rel="stylesheet" href="<?php echo get_stylesheet_uri(); ?>"> </head> <body <?php body_class(); ?>> <header class="header"> <a href="<?php echo esc_url(home_url('/')); ?>" id="logo"><?php bloginfo('name'); ?></a> </header>
Y el footer.php
:
<footer class="footer"> <p>© <?php echo date('Y'); ?> <?php bloginfo('name'); ?></p> </footer> <?php wp_footer(); ?> </body> </html>
Vista de Libros
Y para terminar la plantilla de los libros que comprobaremos si hay cada metabox rellena, sino no lo mostramos.
<?php get_header(); ?> <div id="content"> <?php while (have_posts()) : the_post(); ?> <section <?php post_class('content content-libro'); ?>> <?php $autores = get_post_meta(get_the_ID(), 'autores', true); $anio_publicacion = get_post_meta(get_the_ID(), 'anio_publicacion', true); $genero = get_the_terms(get_the_ID(), 'genero'); if (has_post_thumbnail()) { echo '<div class="content-libro-img col6-12 col12-12-s">' . get_the_post_thumbnail(get_the_ID(), 'large') . '</div>'; } ?> <div class="content-libro-info col6-12 col12-12-s"> <h1><?php the_title(); ?></h1> <ul> <?php if ($autores) { echo '<li><h2>' . $autores . '</h2></li>'; } if ($anio_publicacion) { echo '<p><strong>Año de publicación:</strong> ' . $anio_publicacion . '</p>'; } if ($genero) { echo '<p><strong>Género:</strong> '; foreach ($genero as $term) { echo $term->name . ' '; } echo '</p>'; } ?> </ul> <?php the_content(); ?> </div> </section> <?php endwhile; ?> </div> <?php get_footer(); ?>