single - {$ post_type} - {slug} .php pour les types de publication personnalisés

20

Ma partie préférée de la hiérarchie des modèles Wordpress est la possibilité de créer rapidement des fichiers de modèle pour les pages par slug, sans avoir à modifier la page dans Wordpress pour sélectionner un modèle.

Nous pouvons actuellement le faire:

page- {slug} .php

Mais j'aimerais pouvoir faire ceci:

single- {post_type} - {slug} .php

Pour que, par exemple, dans un type de publication appelé review, je puisse créer un modèle pour une publication intitulée "Ma grande critique" sursingle-review-my-great-review.php

Quelqu'un a-t-il déjà mis cela en place? single-{post_type}-{slug}.php

super vrai
la source
Jamais utilisé une telle configuration auparavant, mais si c'est trop compliqué, pourquoi ne pas simplement créer un fichier modèle et l'associer à la revue en question.
Shane
WP 3.4 récupère automatiquement single-{post_type}-{slug}.php, donc la mise à niveau vers WP 3.4 est une autre option.
yitwail

Réponses:

19

A) La base du noyau

Comme vous pouvez le voir dans l' explication de la hiérarchie des modèles Codex , single-{$post_type}.phpest déjà pris en charge.


B) Extension de la hiérarchie principale

Maintenant, il y a volontiers des filtres et des crochets à l'intérieur /wp-includes/template-loader.php.

  • do_action('template_redirect');
  • apply_filters( 'template_include', $template )
  • ET: un filtre spécifique à l'intérieur get_query_template( $type, ... )nommé:"$type}_template"

B.1) Comment ça marche

  1. A l' intérieur du fichier du chargeur de modèle, le modèle se charge par une requête var / wp_query conditionnelle: is_*().
  2. Le conditionnel se déclenche alors (dans le cas d'un modèle "unique"): is_single() && $template = get_single_template()
  3. Cela déclenche alors get_query_template( $type, $templates ), où $typeestsingle
  4. Ensuite, nous avons le "{$type}_template"filtre

C) La solution

Comme nous voulons uniquement étendre la hiérarchie avec un modèle qui est chargé avant le "single-{$object->post_type}.php"modèle réel , nous allons intercepter la hiérarchie et ajouter un nouveau modèle au début du tableau des modèles.

// 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 );

REMARQUE: (Si vous souhaitez utiliser autre chose que le slug d'objets par défaut) Vous devrez ajuster en $slugfonction de votre structure de permalien. Utilisez simplement ce dont vous avez besoin dans le monde (object) $post.

Billets Trac

Comme l'approche ci-dessus n'est actuellement pas prise en charge (vous pouvez uniquement filtrer le chemin localisé absolu de cette façon), voici une liste de tickets Trac:

kaiser
la source
Je veux tester cela, mais il semble qu'il y ait quelque chose qui manque dans votre ligne add_filter à la fin.
superfue
@supertrue Bonne prise. :) J'ai trouvé un autre manquant )à l'intérieur du filtre. Fixé. Vous souhaitez peut-être échanger le tiret avec un soulignement avant le slug à l'intérieur du modèle. Juste pour permettre au suffixe de mieux ressortir lorsque vous regardez les modèles.
kaiser
Provoque cette erreur sur le site: Avertissement: array_unshift () [function.array-unshift]: Le premier argument doit être un tableau dans [ligne contenant array_unshift]
superfue
D'accord, mais quelque chose d'autre intercepte les modèles principaux. La fonction fonctionne bien et $templatesest un tableau. Voir les fonctions de base dans cette boîte à pâte (pas de date d'expiration). Assurez-vous de tester cela avec une installation sans plugins et le thème par défaut. Activez ensuite les uns après les autres et voyez si l'erreur persiste.
kaiser
Oui, j'ai débogué ceci et je récupère le chemin absolu final du premier modèle trouvé sous forme de chaîne. Je vais devoir en parler à un développeur principal avant de modifier la réponse. Aussi: j'ai mélangé quelque chose: slugn'est disponible que pour les termes et les taxonomies. Vous devez remplacer $post->post_namepar ce qui correspond à votre structure de permalien. Actuellement, il n'y a aucun moyen de le faire automatiquement pour tous les cas en récupérant et en remplaçant le chemin en fonction de votre structure perma et de vos règles de réécriture. Attendez-vous à une autre mise à jour.
kaiser
4

Après l' image de la hiérarchie des modèles , je ne vois pas une telle option.

Voici donc comment j'y arriverais:

Solution 1 (la meilleure à mon avis)

Créez un fichier modèle et associez-le à la révision

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

En ajoutant le modèle de fichier php dans votre répertoire de thèmes, il apparaîtrait comme une option de modèle dans la page d'édition de votre article.

Solution 2

Cela pourrait probablement être réalisé en utilisant le template_redirectcrochet.

Dans le fichier 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' );

ÉDITER

file_existsChèque ajouté

Shane
la source
Pourquoi tu exit;y es ?
kaiser
@kaiser Doit avoir été dans le tutoriel que j'ai suivi à l'époque, si ce n'est pas nécessaire, je le supprimerai.
Shane
1
@kaiser: le exit()est nécessaire pour empêcher le chargement du modèle par défaut.
scribu
La solution 1 ne fonctionnera que pour les pages, pas les publications.
IXN
2

La meilleure réponse (il y a 4 ans) ne fonctionne plus, mais le codex WordPress a la solution ici :

<?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
la source
1

Utiliser des modèles de page

Une autre approche pour l'évolutivité serait de dupliquer la fonctionnalité de liste déroulante du modèle de page sur le pagetype de publication pour votre type de publication personnalisé.

Code réutilisable

La duplication dans le code n'est pas une bonne pratique. Au fil du temps, cela peut provoquer un ballonnement sévère dans une base de code, ce qui rend la gestion d'un développeur très difficile. Au lieu de créer un modèle pour chaque slug, vous aurez probablement besoin d'un modèle un-à-plusieurs qui peut être réutilisé au lieu d'un post-à-modèle un-à-un.

Le code

# 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');

C'est une réponse un peu tardive, mais j'ai pensé que ce serait utile car personne sur le Web n'a documenté cette approche pour autant que je sache. J'espère que cela aide quelqu'un.

Brian Fegter
la source
1

Dans mon cas, j'ai des types de publication personnalisés Album et Track liés par une taxonomie d'album. Je voulais pouvoir utiliser différents modèles de single pour les publications d'album et de piste en fonction de leur taxonomie d'album.

Sur la base de la réponse de Kaiser ci-dessus, j'ai écrit ce code. Ça marche bien.
Remarque. Je n'avais pas besoin de l'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 );
}

Je peux maintenant créer des modèles nommés single-gregory-cpt-track-tax-serendipity.php et single-gregory-cpt-album-tax-serendipity.php et WP les utilisera automatiquement; 'tax-serendipity' est le slug pour le premier terme de taxonomie d'Album.

pour référence, le hook de filtre 'single_template' est déclaré dans:
/wp-includes/theme.php:get_query_template()

Merci Kaiser pour l'exemple de code.

À la vôtre, Gregory

Gregory
la source
Salut Greg - bienvenue au WPSE. Veuillez uniquement publier les réponses comme réponses aux questions - pas les questions complémentaires. Si vous avez une question à laquelle aucune réponse existante ne répond et qui est trop volumineuse pour un commentaire, veuillez ouvrir une autre question :)
Stephen Harris
1
la question chaîne / tableau a été supprimée :-)
Gregory
1
"Merci Kaiser pour l'exemple de code." - Vous êtes les bienvenus.
kaiser
Est-ce que ça marche pour toi? tout d'abord, '$ template' ne devrait pas être commenté dans votre code .. et je pense qu'au lieu de '$ album_tax [0] -> slug' il devrait y avoir '$ object-> post_name', n'est-ce pas?
gregmatys
correction de la ligne $ template. Merci. $ object-> post_name? non. cela retournerait le slug du post, mais j'ai besoin du slug de l'album auquel le post est lié.
Gregory
0

Mise à jour du code Brians, j'ai constaté que lorsque la liste déroulante n'était pas utilisée, l'option de modèle "par défaut" était enregistrée dans wp_page_template, ce qui l'a amené à essayer de trouver un modèle appelé par défaut. cette modification vérifie simplement l'option "par défaut" lors de l'enregistrement et supprime la méta post à la place (utile si vous avez redéfini l'option de modèle par défaut)

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

if (esc_attr ($ _ POST ['_ wp_page_template']) === "default"):
    delete_post_meta ($ post_id, '_wp_page_template');
autre :
    update_post_meta ($ post_id, '_wp_page_template', esc_attr ($ _ POST ['_ wp_page_template']));
fin si;
}
marque
la source