single - {$ post_type} - {slug} .php para tipos de publicaciones personalizadas

20

Mi parte favorita de la jerarquía de plantillas de Wordpress es la capacidad de crear rápidamente archivos de plantilla para páginas mediante slug, sin tener que editar la página en Wordpress para seleccionar una plantilla.

Actualmente podemos hacer esto:

page- {slug} .php

Pero me gustaría poder hacer esto:

single- {post_type} - {slug} .php

De modo que, por ejemplo, en un tipo de publicación llamado review, podría hacer una plantilla para una publicación llamada "Mi gran revisión" ensingle-review-my-great-review.php

¿Alguien ha configurado esto antes? single-{post_type}-{slug}.php

supertrue
fuente
Nunca antes usé una configuración de este tipo, pero si es demasiado complicado, ¿por qué no hacer un archivo de plantilla y asociarlo a la revisión en cuestión?
Shane
WP 3.4 recupera automáticamente single-{post_type}-{slug}.php, por lo que actualizar a WP 3.4 es otra opción.
yitwail

Respuestas:

19

A) La base en el núcleo

Como puede ver en la explicación de la Jerarquía de plantillas del Codex , single-{$post_type}.phpya es compatible.


B) Extendiendo la Jerarquía central

Ahora con mucho gusto hay algunos filtros y ganchos dentro /wp-includes/template-loader.php.

  • do_action('template_redirect');
  • apply_filters( 'template_include', $template )
  • Y: un filtro específico dentro get_query_template( $type, ... )llamado:"$type}_template"

B.1) Cómo funciona

  1. Dentro del archivo cargador de plantillas, la plantilla se carga por una consulta var / WP_Query condicional: is_*().
  2. El condicional entonces se dispara (en el caso de una plantilla "única"): is_single() && $template = get_single_template()
  3. Esto desencadena entonces get_query_template( $type, $templates ), ¿dónde $typeestásingle
  4. Luego tenemos el "{$type}_template"filtro

C) La solución

Como solo queremos extender la jerarquía con una plantilla que se carga antes que la "single-{$object->post_type}.php"plantilla real , interceptaremos la jerarquía y agregaremos una nueva plantilla al comienzo de la matriz de plantillas.

// Extend the hierarchy
function add_posttype_slug_template( $templates )
{

    $object = get_queried_object();

    // New 
    $templates[] = "single-{$object->post_type}-{$object->post_name}.php";
    // Like in core
    $templates[] = "single-{$object->post_type}.php";
    $templates[] = "single.php";

    return locate_template( $templates );    
}
// Now we add the filter to the appropriate hook
function intercept_template_hierarchy()
{
    add_filter( 'single_template', 'add_posttype_slug_template', 10, 1 );
}
add_action( 'template_redirect', 'intercept_template_hierarchy', 20 );

NOTA: (Si desea usar algo diferente a la babosa de objetos predeterminada) Tendrá que ajustar de $slugacuerdo con su estructura de enlace permanente. Solo usa lo que necesites de lo global (object) $post.

Entradas Trac

Como el enfoque anterior no es compatible actualmente (solo puede filtrar la ruta localizada de esta manera), aquí hay una lista de tickets de trac:

emperador
fuente
Quiero probar esto, pero parece que falta algo en su línea add_filter al final.
supertrue
@supertrue Buena captura. :) Encontró otra falta )dentro del filtro. Fijo. Tal vez desee intercambiar el guión con un subrayado antes de la babosa dentro de la plantilla. Solo para que el sufijo se destaque mejor al mirar las plantillas.
kaiser
Provoca este error en todo el sitio: Advertencia: array_unshift () [function.array-unshift]: El primer argumento debe ser una matriz en [línea que contiene array_unshift]
supertrue
Ok, pero entonces algo más está interceptando las plantillas principales. La función funciona bien y $templateses una matriz. Vea las funciones principales en este pastebin (sin fecha de vencimiento). Asegúrese de probar esto con una instalación sin complementos y el tema predeterminado. Luego, active uno tras otro y vea si el error aún se produce.
Kaiser
Sí, depuré esto y obtengo la ruta absoluta final de la primera plantilla encontrada como cadena. Tendré que hablar con algún desarrollador principal sobre esto, antes de modificar la respuesta. Además: mezclé algo: slugsolo está disponible para términos y taxonomías. Debe reemplazarlo $post->post_namecon lo que se ajuste a su estructura de enlace permanente. Actualmente no hay forma de hacer esto automáticamente para todos los casos con recuperar y reemplazar la ruta dependiendo de su estructura perma y reglas de reescritura. Espere otra actualización.
Kaiser
4

Siguiendo la imagen de la Jerarquía de plantilla , no veo esa opción.

Así que aquí está cómo lo haría:

Solución 1 (Mejor en mi opinión)

Haga un archivo de plantilla y asócielo a la revisión.

 <?php
 /*
 Template Name: My Great Review
 */
 ?>

Al agregar el archivo php de plantilla en su directorio de temas, aparecería como una opción de plantilla en la página de edición de su publicación.

Solución 2

Esto probablemente podría lograrse usando el template_redirectgancho.

En el archivo functions.php:

 function my_redirect()
 {
      global $post;

      if( get_post_type( $post ) == "my_cpt" && is_single() )
      {
           if( file_exists( get_template_directory() . '/single-my_cpt-' . $post->post_name . '.php' ) )
           {
                include( get_template_directory() . '/single-my_cpt-' . $post->post_name . '.php' );
                exit;
           }
      }
 }
 add_action( 'template_redirect', 'my_redirect' );

EDITAR

file_existsCheque agregado

Shane
fuente
¿Por qué estás exit;ahí?
kaiser
@kaiser Debe haber estado en el tutorial que seguí en ese momento, si no es necesario lo eliminaré.
Shane
1
@kaiser: exit()es necesario para evitar cargar la plantilla predeterminada.
scribu
La solución 1 solo funcionará para páginas, no para publicaciones.
IXN
2

La respuesta principal (de hace 4 años) ya no funciona, pero el códice de WordPress tiene la solución aquí :

<?php
function add_posttype_slug_template( $single_template )
{
    $object = get_queried_object();
    $single_postType_postName_template = locate_template("single-{$object->post_type}-{$object->post_name}.php");
    if( file_exists( $single_postType_postName_template ) )
    {
        return $single_postType_postName_template;
    } else {
        return $single_template;
    }
}
add_filter( 'single_template', 'add_posttype_slug_template', 10, 1 );
?>
skladany
fuente
1

Usar plantillas de página

Otro enfoque para la escalabilidad sería duplicar la funcionalidad desplegable de la plantilla de página en el pagetipo de publicación para su tipo de publicación personalizada.

Código reutilizable

La duplicación en el código no es una buena práctica. En el tiempo extra, puede causar una gran hinchazón en una base de código cuando, por lo tanto, es muy difícil de administrar para un desarrollador. En lugar de crear una plantilla para cada slug, lo más probable es que necesites una plantilla de uno a muchos que se pueda reutilizar en lugar de una a una post-a-plantilla.

El código

# Define your custom post type string
define('MY_CUSTOM_POST_TYPE', 'my-cpt');

/**
 * Register the meta box
 */
add_action('add_meta_boxes', 'page_templates_dropdown_metabox');
function page_templates_dropdown_metabox(){
    add_meta_box(
        MY_CUSTOM_POST_TYPE.'-page-template',
        __('Template', 'rainbow'),
        'render_page_template_dropdown_metabox',
        MY_CUSTOM_POST_TYPE,
        'side', #I prefer placement under the post actions meta box
        'low'
    );
}

/**
 * Render your metabox - This code is similar to what is rendered on the page post type
 * @return void
 */
function render_page_template_dropdown_metabox(){
    global $post;
    $template = get_post_meta($post->ID, '_wp_page_template', true);
    echo "
        <label class='screen-reader-text' for='page_template'>Page Template</label>
            <select name='_wp_page_template' id='page_template'>
            <option value='default'>Default Template</option>";
            page_template_dropdown($template);
    echo "</select>";
}

/**
 * Save the page template
 * @return void
 */
function save_page_template($post_id){

    # Skip the auto saves
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
        return;
    elseif ( defined( 'DOING_AJAX' ) && DOING_AJAX )
        return;
    elseif ( defined( 'DOING_CRON' ) && DOING_CRON )
        return;

    # Only update the page template meta if we are on our specific post type
    elseif(MY_CUSTOM_POST_TYPE === $_POST['post_type'])
        update_post_meta($post_id, '_wp_page_template', esc_attr($_POST['_wp_page_template']));
}
add_action('save_post', 'save_page_template');


/**
 * Set the page template
 * @param string $template The determined template from the WordPress brain
 * @return string $template Full path to predefined or custom page template
 */
function set_page_template($template){
    global $post;
    if(MY_CUSTOM_POST_TYPE === $post->post_type){
        $custom_template = get_post_meta($post->ID, '_wp_page_template', true);
        if($custom_template)
            #since our dropdown only gives the basename, use the locate_template() function to easily find the full path
            return locate_template($custom_template);
    }
    return $template;
}
add_filter('single_template', 'set_page_template');

Esta es una respuesta un poco tardía, pero pensé que sería valioso ya que nadie en la web ha documentado este enfoque hasta donde puedo decir. Espero que esto ayude a alguien.

Brian Fegter
fuente
1

En mi caso, tengo tipos de publicaciones personalizadas de Álbum y Pista vinculadas por una taxonomía de Álbum. Quería poder usar diferentes plantillas individuales para las publicaciones de Álbum y Pista dependiendo de su taxonomía de Álbum.

Basado en la respuesta de Kaiser anterior, escribí este código. Funciona bien.
Nota. No necesitaba la add_action ().

// Add an additional template option to the template hierarchy
add_filter( 'single_template', 'add_albumtrack_taxslug_template', 10, 1 );
function add_albumtrack_taxslug_template( $orig_template_path )
{
    // at this point, $orig_template_path is an absolute located path to the preferred single template.

    $object = get_queried_object();

    if ( ! (
        // specify another template option only for Album and Track post types.
        in_array( $object->post_type, array( 'gregory-cpt-album','gregory-cpt-track' )) &&
        // check that the Album taxonomy has been registered.
        taxonomy_exists( 'gregory-tax-album' ) &&
        // get the Album taxonomy term for the current post.
        $album_tax = wp_get_object_terms( $object->ID, 'gregory-tax-album' )
        ))
        return $orig_template_path;

    // assemble template name
    // assumption: only one Album taxonomy term per post. we use the first object in the array.
    $template = "single-{$object->post_type}-{$album_tax[0]->slug}.php";
    $template = locate_template( $template );
    return ( !empty( $template ) ? $template : $orig_template_path );
}

Ahora puedo crear plantillas llamadas single-gregory-cpt-track-tax-serendipity.php y single-gregory-cpt-album-tax-serendipity.php y WP las usará automáticamente; 'tax-serendipity' es la babosa para el primer término de taxonomía del Álbum.

para referencia, el gancho del filtro 'single_template' se declara en:
/wp-includes/theme.php:get_query_template()

Gracias Kaiser por el código de muestra.

Saludos, Gregory

Gregory
fuente
Hola Greg, bienvenido a WPSE. Solo publique respuestas como respuestas a las preguntas, no preguntas de seguimiento. Si tiene una pregunta que no está respondida por una respuesta existente y es demasiado grande para un comentario, abra otra pregunta :)
Stephen Harris
1
la pregunta de cadena / matriz se ha eliminado :-)
Gregory
1
"Gracias Kaiser por el código de muestra". - De nada.
kaiser
¿Funciona para ti? en primer lugar, '$ template' no debería comentarse en su código ... y creo que en lugar de '$ album_tax [0] -> slug' debería haber '$ object-> post_name', ¿no?
gregmatys
arregló la línea $ template. gracias. $ objeto-> post_name? No. eso devolvería la babosa de la publicación, pero necesito la babosa del álbum al que está vinculada la publicación.
Gregory
0

Actualización para el código Brians, descubrí que cuando no se usaba el cuadro desplegable, la opción de plantilla "predeterminada" se guardaba en wp_page_template, lo que hacía que intentara encontrar una plantilla llamada predeterminada. este cambio solo comprueba la opción "predeterminada" al guardar y elimina el mensaje meta en su lugar (útil si cambia la opción de plantilla a la predeterminada)

elseif (MY_CUSTOM_POST_TYPE === $ _POST ['post_type']) {

if (esc_attr ($ _ POST ['_ wp_page_template']) === "predeterminado"):
    delete_post_meta ($ post_id, '_wp_page_template');
más:
    update_post_meta ($ post_id, '_wp_page_template', esc_attr ($ _ POST ['_ wp_page_template']));
terminara si;
}
marca
fuente