single-{$post_type}-{slug}.php per tipi di post personalizzati

2 feb 2012, 18:36:50
Visualizzazioni: 16K
Voti: 22

La mia parte preferita della gerarchia dei template di WordPress è la possibilità di creare rapidamente file di template per le pagine utilizzando lo slug, senza dover modificare la pagina in WordPress per selezionare un template.

Attualmente possiamo fare questo:

page-{slug}.php

Ma mi piacerebbe poter fare questo:

single-{post_type}-{slug}.php

In modo che, ad esempio, in un tipo di post chiamato review, potrei creare un template per un post chiamato "La mia grande recensione" in single-review-la-mia-grande-recensione.php

Qualcuno ha già implementato qualcosa del genere? single-{post_type}-{slug}.php

2
Commenti

Non ho mai utilizzato una configurazione del genere prima d'ora, ma se è troppo complicato, perché non creare semplicemente un file template e associarlo alla recensione in questione.

Shane Shane
2 feb 2012 18:53:50

WP 3.4 recupera automaticamente single-{post_type}-{slug}.php, quindi aggiornare a WP 3.4 è un'altra opzione.

yitwail yitwail
29 lug 2012 20:56:33
Tutte le risposte alla domanda 6
12
20

A) La Base nel Core

Come puoi vedere nel Codex nella spiegazione della Gerarchia dei Template, single-{$post_type}.php è già supportato.


B) Estendere la Gerarchia del Core

Fortunatamente ora ci sono alcuni filtri e hook all'interno di /wp-includes/template-loader.php.

  • do_action('template_redirect');
  • apply_filters( 'template_include', $template )
  • E: un filtro specifico dentro get_query_template( $type, ... ) chiamato: "$type}_template"

B.1) Come Funziona

  1. Dentro il file del template loader, il template viene caricato da una variabile di query/wp_query condizionale: is_*().
  2. La condizione poi attiva (nel caso di un template "single"): is_single() && $template = get_single_template()
  3. Questo attiva poi get_query_template( $type, $templates ), dove $type è single
  4. Poi abbiamo il filtro "{$type}_template"

C) La Soluzione

Visto che vogliamo solo estendere la gerarchia con un template che viene caricato prima dell'attuale template "single-{$object->post_type}.php", intercepteremo la gerarchia e aggiungeremo un nuovo template all'inizio dell'array dei template.

// Estendi la gerarchia
function add_posttype_slug_template( $templates )
{

    $object = get_queried_object();

    // Nuovo 
    $templates[] = "single-{$object->post_type}-{$object->post_name}.php";
    // Come nel core
    $templates[] = "single-{$object->post_type}.php";
    $templates[] = "single.php";

    return locate_template( $templates );    
}
// Ora aggiungiamo il filtro all'hook appropriato
function intercept_template_hierarchy()
{
    add_filter( 'single_template', 'add_posttype_slug_template', 10, 1 );
}
add_action( 'template_redirect', 'intercept_template_hierarchy', 20 );

NOTA: (Se vuoi usare qualcosa di diverso dallo slug predefinito dell'oggetto) Dovrai modificare $slug in base alla tua struttura dei permalink. Basta usare ciò che ti serve dal globale (object) $post.

Ticket Trac

Poiché l'approccio sopra attualmente non è supportato (puoi solo filtrare il percorso assoluto in questo modo), ecco una lista di ticket trac:

2 feb 2012 19:25:45
Commenti

Voglio provare questo, ma sembra che manchi qualcosa nella tua riga add_filter alla fine.

supertrue supertrue
2 feb 2012 22:45:04

@supertrue Ottimo occhio. :) Ho trovato un altro ) mancante all'interno del filtro. Sistemato. Forse vorresti sostituire il trattino con un underscore prima dello slug all'interno del template. Solo per far risaltare meglio il suffisso quando si controllano i template.

kaiser kaiser
3 feb 2012 00:01:12

Causa questo errore sul sito: Avviso: array_unshift() [function.array-unshift]: Il primo argomento dovrebbe essere un array nella [riga contenente array_unshift]

supertrue supertrue
4 feb 2012 02:02:30

Ok, ma allora qualcos'altro sta intercettando i template core. La funzione funziona bene e $templates è un array. Vedi le funzioni core in questo pastebin (senza data di scadenza). Assicurati di testare questo con un'installazione senza plugin e con il tema di default. Poi attivali uno dopo l'altro e verifica se l'errore si ripresenta.

kaiser kaiser
4 feb 2012 02:47:49

Sì, ho debugato e ottengo il percorso assoluto finale del primo template trovato come stringa. Dovrò parlare con qualche sviluppatore core riguardo a questo, prima di modificare la risposta. Inoltre: ho fatto confusione: slug è disponibile solo per termini e tassonomie. Dovresti sostituire $post->post_name con ciò che si adatta alla tua struttura dei permalink. Attualmente non c'è modo di farlo automaticamente per tutti i casi recuperando e sostituendo il percorso in base alla tua struttura dei permalink e alle regole di rewrite. Aspettati un altro aggiornamento.

kaiser kaiser
4 feb 2012 03:19:40

Ti ho lasciato una versione funzionante - comunque questa non è soddisfacente (copiare/incollare l'array core e poi riaggiungerlo all'interno di una funzione personalizzata).

kaiser kaiser
4 feb 2012 03:27:01

questo non sembra aver avuto alcun effetto... la parte finale è corretta? Non vedo che intercept_template_hierarchy venga chiamato da nessuna parte. Cambiando l'ultimo add_posttype_slug_template in intercept_template_hierarchy ha restituito Fatal error: [] operator not supported for strings

supertrue supertrue
7 feb 2012 04:59:47

@supertrue Sì, questo è un problema. Semplicemente non funziona. Sto già parlando con uno sviluppatore core per inserirlo nel ciclo della 3.4. Torna tra qualche giorno e ripubblica questa domanda con un commento qui. Grazie. Nota a margine: il core si comporta in modo estremamente stupido a questo punto, poiché l'array dei template non viene filtrato, ma viene filtrato invece il percorso del template localizzato. Considero il fatto che questo sia effettivamente un bug.

kaiser kaiser
7 feb 2012 05:47:49

@supertrue La discussione è già iniziata. Aspettati un aggiornamento nei prossimi giorni. Se non sarà un aggiornamento, magari sarà un ticket su trac :P

kaiser kaiser
7 feb 2012 12:54:09

@supertrue Dominik Schilling (alias Ocean90) si è aggiunto autonomamente ad alcuni ticket. Ci si aspetta progressi con la versione 3.4.

kaiser kaiser
10 feb 2012 17:19:06

@kaiser la tua soluzione sembra davvero carina ma non funziona ($templates non è un array nel mio caso... ricevo un errore fatale). C'è qualche possibilità di farla funzionare?

gregmatys gregmatys
18 nov 2013 14:54:38

@gregmatys Per favore leggi l'ultimo paragrafo "Trac Tickets".

kaiser kaiser
18 nov 2013 15:00:35
Mostra i restanti 7 commenti
4

Seguendo l'immagine della Gerarchia dei Template, non vedo un'opzione del genere.

Quindi ecco come procederei:

Soluzione 1 (La migliore secondo me)

Crea un file template e associalo alla recensione

 <?php
 /*
 Template Name: La mia fantastica recensione
 */
 ?>

Aggiungendo il file template php nella directory del tuo tema, apparirà come opzione di template nella pagina di modifica del tuo articolo.

Soluzione 2

Questo potrebbe probabilmente essere ottenuto usando l'hook template_redirect.

Nel file 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' );

MODIFICA

Aggiunto controllo file_exists

2 feb 2012 19:06:21
Commenti

Perché usi exit; lì?

kaiser kaiser
2 feb 2012 20:26:36

@kaiser Dev'essere stato in qualche tutorial che ho seguito all'epoca, se non è necessario lo rimuoverò.

Shane Shane
2 feb 2012 21:48:04

@kaiser: Il exit() è necessario per evitare il caricamento del template predefinito.

scribu scribu
3 feb 2012 02:30:35

La soluzione 1 funzionerà solo per le pagine, non per gli articoli.

IXN IXN
7 nov 2015 14:18:56
0

La risposta più votata (di 4 anni fa) non funziona più, ma il codex di WordPress fornisce la soluzione qui:

<?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 );
?>
9 giu 2016 23:39:00
5

Nel mio caso, ho dei custom post type Album e Traccia collegati da una tassonomia Album. Volevo poter utilizzare template Single diversi per i post Album e Traccia in base alla loro tassonomia Album.

Sulla base della risposta di Kaiser sopra, ho scritto questo codice. Funziona bene.
Nota: Non avevo bisogno dell'add_action().

// Aggiunge un'opzione di template aggiuntiva alla gerarchia dei template
add_filter( 'single_template', 'add_albumtrack_taxslug_template', 10, 1 );
function add_albumtrack_taxslug_template( $orig_template_path )
{
    // a questo punto, $orig_template_path è un percorso assoluto localizzato al template single preferito.

    $object = get_queried_object();

    if ( ! (
        // specifica un'altra opzione di template solo per i post type Album e Traccia.
        in_array( $object->post_type, array( 'gregory-cpt-album','gregory-cpt-track' )) &&
        // verifica che la tassonomia Album sia stata registrata.
        taxonomy_exists( 'gregory-tax-album' ) &&
        // ottiene il termine della tassonomia Album per il post corrente.
        $album_tax = wp_get_object_terms( $object->ID, 'gregory-tax-album' )
        ))
        return $orig_template_path;

    // compone il nome del template
    // presupposto: solo un termine di tassonomia Album per post. usiamo il primo oggetto nell'array.
    $template = "single-{$object->post_type}-{$album_tax[0]->slug}.php";
    $template = locate_template( $template );
    return ( !empty( $template ) ? $template : $orig_template_path );
}

Ora posso creare template chiamati single-gregory-cpt-track-tax-serendipity.php e single-gregory-cpt-album-tax-serendipity.php e WP li utilizzerà automaticamente; 'tax-serendipity' è lo slug per il primo termine della tassonomia Album.

per riferimento, l'hook del filtro 'single_template' è dichiarato in:
/wp-includes/theme.php: get_query_template()

Grazie a Kaiser per il codice di esempio.

Saluti, Gregory

18 apr 2012 09:46:11
Commenti

Ciao Greg - benvenuto su WPSE. Per favore, pubblica solo risposte alle domande - non domande di follow-up. Se hai una domanda che non è stata risposta da una risposta esistente ed è troppo lunga per un commento, per favore apri un'altra domanda :)

Stephen Harris Stephen Harris
18 apr 2012 18:07:19

la domanda su stringhe/array è stata rimossa :-)

Gregory Gregory
18 apr 2012 19:04:13

"Grazie Kaiser per il codice di esempio." - Prego.

kaiser kaiser
23 ott 2012 16:01:37

funziona per te? prima di tutto, '$template' non dovrebbe essere commentato nel tuo codice.. e penso che invece di '$album_tax[0]->slug' ci dovrebbe essere '$object->post_name', non è vero?

gregmatys gregmatys
18 nov 2013 16:14:14

ho sistemato la riga con $template. grazie. $object->post_name? no. quello restituirebbe lo slug del post, ma a me serve lo slug dell'album a cui il post è collegato.

Gregory Gregory
19 nov 2013 01:21:10
0

Utilizzare i Template di Pagina

Un altro approccio per la scalabilità sarebbe duplicare la funzionalità del menu a discesa dei template di pagina sul tipo di post page per il tuo custom post type.

Codice Riusabile

La duplicazione nel codice non è una buona pratica. Nel tempo può causare un grave gonfiamento della codebase rendendola poi molto difficile da gestire per uno sviluppatore. Invece di creare un template per ogni singolo slug, molto probabilmente avrai bisogno di un template uno-a-molti che possa essere riutilizzato invece di un rapporto uno-a-uno tra post e template.

Il Codice

# Definisci la stringa del tuo custom post type
define('MY_CUSTOM_POST_TYPE', 'my-cpt');

/**
 * Registra il 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', #Preferisco il posizionamento sotto il meta box delle azioni del post
        'low'
    );
}

/**
 * Renderizza il tuo metabox - Questo codice è simile a quello renderizzato sul tipo di post page
 * @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'>Template Pagina</label>
            <select name='_wp_page_template' id='page_template'>
            <option value='default'>Template Predefinito</option>";
            page_template_dropdown($template);
    echo "</select>";
}

/**
 * Salva il template della pagina
 * @return void
 */
function save_page_template($post_id){

    # Salta i salvataggi automatici
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
        return;
    elseif ( defined( 'DOING_AJAX' ) && DOING_AJAX )
        return;
    elseif ( defined( 'DOING_CRON' ) && DOING_CRON )
        return;

    # Aggiorna il meta del page template solo se siamo sul nostro specifico 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');


/**
 * Imposta il template della pagina
 * @param string $template Il template determinato dal cervello di WordPress
 * @return string $template Percorso completo al template di pagina predefinito o personalizzato
 */
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)
            #poiché il nostro menu a discesa restituisce solo il nome base, usa la funzione locate_template() per trovare facilmente il percorso completo
            return locate_template($custom_template);
    }
    return $template;
}
add_filter('single_template', 'set_page_template');

Questa è una risposta un po' tardiva, ma ho pensato che potesse essere utile dato che nessuno sul web ha documentato questo approccio per quanto ne so. Spero che questo possa aiutare qualcuno.

13 ott 2012 09:29:41
0

Aggiornamento per il codice di Brian, ho scoperto che quando la casella a discesa non veniva utilizzata l'opzione template "default" veniva salvata in wp_page_template causando il tentativo di trovare un template chiamato default. Questa modifica verifica semplicemente la presenza dell'opzione "default" durante il salvataggio e invece cancella il post meta (utile se hai cambiato l'opzione del template tornando a default)

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

if ( esc_attr($_POST['_wp_page_template']) === "default" ) :
    delete_post_meta($post_id, '_wp_page_template');
else :
    update_post_meta($post_id, '_wp_page_template', esc_attr($_POST['_wp_page_template']));
endif;
}
29 mag 2014 21:20:33