Como crear una plantilla single para un CPT en PHP en WordPress

Contents

Introducción

Este tutorial explica con todo lujo de detalles cómo crear una plantilla single para un Custom Post Type (CPT) en WordPress usando PHP. Cubriremos desde el registro del CPT hasta la creación del archivo single-{post_type}.php, alternativas (uso de template_include), buenas prácticas de seguridad y rendimiento, y ejemplos completos listos para copiar y adaptar.

Requisitos previos

  • Un tema (preferiblemente un child theme) donde puedas añadir archivos PHP.
  • Acceso al archivo functions.php del tema o a un plugin para registrar el CPT.
  • Conocimientos básicos de PHP y del loop de WordPress.

1) Registrar el Custom Post Type

Para que WordPress tenga un tipo de contenido personalizado, regístralo con register_post_type. Ejemplo completo para un CPT llamado book:

 Libros,
        singular_name      => Libro,
        menu_name          => Libros,
        name_admin_bar     => Libro,
    )

    args = array(
        labels             => labels,
        public             => true,
        has_archive        => true,
        rewrite            => array(slug => libros),
        show_in_rest       => true,
        supports           => array(title, editor, thumbnail, excerpt, comments),
        capability_type    => post,
    )

    register_post_type(book, args)
}
?>

Importante: si cambias el slug o registras el CPT por primera vez, haz flush de las reglas de reescritura. Nunca llames a flush_rewrite_rules() en cada carga úsalo sólo en la activación del plugin o al actualizar la configuración.


2) Jerarquía de plantillas (cómo decide WordPress qué archivo usar)

Para un solo ítem del CPT, WordPress busca plantillas en este orden:

Orden Plantilla Descripción
1 single-{post_type}.php Plantilla específica para el tipo de contenido (ej. single-book.php)
2 single.php Plantilla genérica single
3 singular.php Plantilla singular para cualquier tipo
4 index.php Fallback final

3) Crear single-{post_type}.php en tu tema (ejemplo práctico)

Crea un archivo llamado single-book.php dentro del directorio del tema (mejor en un child theme). Ejemplo minimal y seguro que sabe mostrar título, contenido, campos personalizados y taxonomías:

 esc_attr(get_the_title())) )
        }

        // Contenido (escapado permitiendo HTML básico)
        echo wp_kses_post( get_the_content() )

        // Campos personalizados (ejemplo: isbn)
        isbn = get_post_meta( get_the_ID(), isbn, true )
        if ( isbn ) {
            echo ltpgtltstronggtISBN:lt/stronggt  . esc_html( isbn ) . lt/pgt
        }

        // Taxonomías asociadas (ejemplo: genre)
        terms = get_the_terms( get_the_ID(), genre )
        if ( ! empty( terms )  ! is_wp_error( terms ) ) {
            out = array()
            foreach ( terms as term ) {
                out[] = lta href=quot . esc_url( get_term_link(term) ) . quotgt . esc_html( term->name ) . lt/agt
            }
            echo ltpgtltstronggtGénero:lt/stronggt  . implode(, , out) . lt/pgt
        }

        // Navegación entre posts
        previous_post_link(ltdivgt%linklt/divgt,lt Anterior: %title)
        next_post_link(ltdivgt%linklt/divgt,Siguiente: %title gt)

        // Comentarios
        if ( comments_open()  get_comments_number() ) {
            comments_template()
        }

    endwhile
endif

get_footer()
?>

Este ejemplo usa funciones de escape: esc_html, esc_url, wp_kses_post y esc_attr para evitar problemas XSS. Ajusta la maquetación HTML según tu tema dentro del archivo (aquí las etiquetas HTML reales están dentro del PHP para mayor control).

Usar get_template_part para organizar el código

Si prefieres mantener la lógica separada, crea un partial en template-parts/content-book.php y en single-book.php solo invoca get_template_part:



ltarticle id=post-lt?php the_ID() ?gtgt
    lth1gtlt?php the_title() ?gtlt/h1gt
    lt?php the_content() ?gt
lt/articlegt

4) Alternativa: usar template_include para cargar plantillas desde un plugin o carpeta personalizada

Si prefieres controlar la plantilla desde un plugin o cargar plantillas ubicadas fuera del tema, usa el filtro template_include. Ejemplo:


Con esto controlas exactamente qué archivo servirá la vista del CPT aun cuando el tema no tenga un single-{post_type}.php.

5) Mostrar campos personalizados (ACF u get_post_meta)

Si usas ACF (Advanced Custom Fields), lo habitual es llamarlo con get_field(campo). Si no, usa get_post_meta. Ejemplo seguro:


6) Seguridad y buenas prácticas

  • Escapa siempre cualquier salida con esc_html, esc_attr, esc_url o wp_kses_post según corresponda.
  • No confíes en los inputs del usuario: sanitiza con sanitize_text_field, wp_strip_all_tags, etc., cuando guardes meta.
  • Evita consultas innecesarias: usa datos del loop actual en lugar de nuevos WP_Query cuando sea posible.
  • Usa nonces para formularios que modifiquen datos.
  • Soporte responsive y accesibilidad: añade atributos alt en imágenes y usa etiquetas semánticas.

7) Rendimiento

  • Cachea resultados pesados con transients o con object cache.
  • Evita múltiples consultas a la base de datos dentro del loop agrupa peticiones si es posible.
  • Si necesitas mostrar relacionados o metadatos complejos, pre-cálculalos mediante cron o al guardar el post.

8) Comprobaciones y resolución de problemas

  1. Plantilla no carga: asegúrate de que el archivo se llama exactamente single-{post_type}.php y está en la raíz del tema (o child theme).
  2. Permalinks 404: ve a Ajustes → Enlaces permanentes y guarda para forzar el flush, o asegúrate de haber ejecutado flush_rewrite_rules() al activar el CPT.
  3. WordPress carga single.php en lugar de single-book.php: comprueba errores de nombre del post type (book vs books), y la prioridad de carga del child/parent theme.
  4. Campos personalizados vacíos: verifica la clave exacta del meta (mayúsculas/minúsculas) y el método de guardado (ACF vs manual).
  5. Errores de permisos: si no ves entradas en el admin, revisa las capacidades y el parámetro public y show_in_menu al registrar el CPT.

9) Ejemplo completo: register single partial

Aquí un resumen con los archivos implicados:

  • functions.php: registra el CPT (ejemplo ya mostrado).
  • single-book.php: invoca get_template_part.
  • template-parts/content-book.php: muestra el HTML del libro (título, contenido, meta).

ltarticle id=post-lt?php the_ID() ?gt role=articlegt
    ltheadergt
        lth1gtlt?php the_title() ?gtlt/h1gt
    lt/headergt

    ltsection class=entry-contentgt
        lt?php the_content() ?gt
    lt/sectiongt

    ltfooter class=entry-metagt
        lt?php
        isbn = get_post_meta( get_the_ID(), isbn, true )
        if ( isbn ) {
            echo ltpgtltstronggtISBN:lt/stronggt  . esc_html( isbn ) . lt/pgt
        }
        ?gt
    lt/footergt
lt/articlegt

10) Consideraciones finales

Crear una plantilla single para un CPT en WordPress es sencillo si sigues la jerarquía de plantillas y mantienes el código organizado y seguro. Usa single-{post_type}.php para la personalización directa, o template_include si necesitas controlar la plantilla desde un plugin. Separa vistas con get_template_part para mantener el mantenimiento fácil y reutilizable.

Recuerda

El archivo se publica tal cual en tu sitio: aplica las prácticas de escape y saneamiento mostradas, verifica los nombres exactos de CPT y rutas, y mantén el código modular para facilitar actualizaciones del tema.



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Deja una respuesta

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