Applicare un template a un custom post type

5 ott 2015, 20:14:44
Visualizzazioni: 24.2K
Voti: 3

Oggi ho avuto un cliente che voleva un template personalizzato per ogni pagina e sezione all'interno. Ho proposto Laravel custom sheet, ma lui voleva WordPress, poiché sembra più facile da gestire (non dalla mia esperienza).

.. Fin qui tutto bene. Sono un po' confuso su questo punto.

Ho deciso che la mia entità principale sarebbe stata la pagina, così da poter applicare template diversi a ogni pagina. Poi ho delle sezioni all'interno di questa pagina, che non dovrebbero essere hardcoded. L'utente dovrebbe avere la possibilità di scegliere il layout della sezione (template), e rimuovere o riordinare le sezioni all'interno della pagina corrente. E infine ho i post che sono l'entità più piccola nel sito web. I post verrebbero visualizzati come colonne come in bootstrap (col-md-2, col-lg-6), ecc..

Ho deciso di creare un custom post type da utilizzare come sezione, ma poi ho letto che i post type non possono avere template, solo le pagine possono averli. Questo ha compromesso il mio piano finora e ho speso 5 ore nella ricerca di una soluzione (senza via d'uscita). Quindi ho bisogno di un'altra strategia per fare questo. Ho bisogno di template per due entità.

Qualcuno può suggerire una soluzione a questo problema? (Ti offrirò una birra!)

MODIFICA:

Per creare il mio custom post type, uso un plugin in WordPress chiamato 'Custom Post Type UI', ovviamente c'è un altro modo, inserendo un breve snippet di codice nel file functions.php, ma non lo tratterò qui.

0
Tutte le risposte alla domanda 4
1
20

A partire da WordPress 4.7, è stato introdotto il supporto per template multipli nei Custom Post Type.

Per rendere disponibile un template per il tuo Custom Post Type, devi aggiungere questa intestazione nella sezione meta del file del template:

Template Post Type: post, foo, bar 

Ad esempio, supponiamo che il tuo Custom Post Type sia chiamato "my_events" e che tu voglia rendere disponibile un template chiamato "Fullwidth" sia per le Pagine che per il tuo Custom Post Type.

Da questo:

/**
 * Template Name: Fullwidth
 * 
 * Template Description...
 **/

A questo:

/**
 * Template Name: Fullwidth
 * Template Post Type: page, my_events
 * 
 * Template Description...
 **/

Maggiori informazioni: Template per Post Type in 4.7 dal Core di WordPress

13 gen 2017 14:14:19
Commenti

Perfetto! Questa è la risposta migliore a partire dal 2017.

Ryan Ryan
15 mar 2017 23:24:34
3

Normalmente non seguirei una risposta così dettagliata come quella di @Milo con un post come questo. Siccome avevo già scritto questo codice per un altro progetto, ho pensato di condividerlo.

Il codice seguente fa tutto ciò che @Milo ha riassunto nella sua risposta e l'ho utilizzato in diversi progetti con grande successo.

Ecco il riassunto di ciò che fa:

1) Agganciarsi all'azione 'add_meta_boxes' per mostrare una nuova meta box personalizzata nella schermata di modifica (tranne che per il post type nativo 'page').

2) Agganciarsi all'azione 'admin_menu' per rimuovere la meta box nativa 'Page Attributes' (tranne che per il post type nativo 'page').

3) Costruire la meta box personalizzata per sostituire la funzionalità della meta box nativa 'Page Attributes'. Questo include campi per definire il PARENT, il TEMPLATE e l'ORDER di qualsiasi post type personalizzato che hai inizializzato.

4) Agganciarsi all'azione 'save_post' per salvare la selezione del template nei meta del post.

5) Agganciarsi al filtro 'single_template' per caricare il tuo template personalizzato invece del template predefinito di WordPress.

Ecco il codice completamente funzionante che puoi copiare/incollare:

/** Custom Post Type Template Selector **/
function cpt_add_meta_boxes() {
    $post_types = get_post_types();
    foreach( $post_types as $ptype ) {
        if ( $ptype !== 'page') {
            add_meta_box( 'cpt-selector', 'Attributes', 'cpt_meta_box', $ptype, 'side', 'core' );
        }
    }
}
add_action( 'add_meta_boxes', 'cpt_add_meta_boxes' );

function cpt_remove_meta_boxes() {
    $post_types = get_post_types();
    foreach( $post_types as $ptype ) {
        if ( $ptype !== 'page') {
            remove_meta_box( 'pageparentdiv', $ptype, 'normal' );
        }
    }
}
add_action( 'admin_menu' , 'cpt_remove_meta_boxes' );

function cpt_meta_box( $post ) {
    $post_meta = get_post_meta( $post->ID );
    $templates = wp_get_theme()->get_page_templates();

    $post_type_object = get_post_type_object($post->post_type);
    if ( $post_type_object->hierarchical ) {
        $dropdown_args = array(
            'post_type'        => $post->post_type,
            'exclude_tree'     => $post->ID,
            'selected'         => $post->post_parent,
            'name'             => 'parent_id',
            'show_option_none' => __('(no parent)'),
            'sort_column'      => 'menu_order, post_title',
            'echo'             => 0,
        );

        $dropdown_args = apply_filters( 'page_attributes_dropdown_pages_args', $dropdown_args, $post );
        $pages = wp_dropdown_pages( $dropdown_args );

        if ( $pages ) { 
            echo "<p><strong>Parent</strong></p>";
            echo "<label class=\"screen-reader-text\" for=\"parent_id\">Parent</label>";
            echo $pages;
        }
    }

    // Template Selector
    echo "<p><strong>Template</strong></p>";
    echo "<select id=\"cpt-selector\" name=\"_wp_page_template\"><option value=\"default\">Default Template</option>";
    foreach ( $templates as $template_filename => $template_name ) {
        if ( $post->post_type == strstr( $template_filename, '-', true) ) {
            if ( isset($post_meta['_wp_page_template'][0]) && ($post_meta['_wp_page_template'][0] == $template_filename) ) {
                echo "<option value=\"$template_filename\" selected=\"selected\">$template_name</option>";
            } else {
                echo "<option value=\"$template_filename\">$template_name</option>";
            }
        }
    }
    echo "</select>";

    // Page order
    echo "<p><strong>Order</strong></p>";
    echo "<p><label class=\"screen-reader-text\" for=\"menu_order\">Order</label><input name=\"menu_order\" type=\"text\" size=\"4\" id=\"menu_order\" value=\"". esc_attr($post->menu_order) . "\" /></p>";
}

function save_cpt_template_meta_data( $post_id ) {

    if ( isset( $_REQUEST['_wp_page_template'] ) ) {
        update_post_meta( $post_id, '_wp_page_template', $_REQUEST['_wp_page_template'] );
    }
}
add_action( 'save_post' , 'save_cpt_template_meta_data' );

function custom_single_template($template) {
    global $post;

    $post_meta = ( $post ) ? get_post_meta( $post->ID ) : null;
    if ( isset($post_meta['_wp_page_template'][0]) && ( $post_meta['_wp_page_template'][0] != 'default' ) ) {
        $template = get_template_directory() . '/' . $post_meta['_wp_page_template'][0];
    }

    return $template;
}
add_filter( 'single_template', 'custom_single_template' );
/** END Custom Post Type Template Selector **/

L'unica assunzione che ho fatto è che i tuoi template seguano una convenzione di denominazione best practice come:

posttype-templatename.php

Ad esempio, potresti definire alcuni template personalizzati per un post type personalizzato "Event" utilizzando la seguente convenzione di denominazione all'interno del tuo tema:

event-standard.php
event-allday.php
event-recurring.php

Questo codice è abbastanza intelligente da permettere solo ai template "event" di essere applicati al post type Event. In altre parole, un template chiamato "section-video.php" non sarebbe mai visibile per il post type Event. Quel template apparirebbe invece come opzione per il post type "Section".

Per rimuovere questa funzionalità devi semplicemente rimuovere la logica condizionale dal codice sopra:

if ( $post->post_type == strstr( $template_filename, '-', true) ) {  }
5 ott 2015 22:14:02
Commenti

Per motivi di utilità, ho contrassegnato la tua risposta come 'Migliore Risposta', perché spiega in modo molto chiaro 'come applicare un template a un custom post type'. E ovviamente fornisce un frammento di codice molto utile che posso semplicemente copiare e incollare ( il vero sogno ) nel mio functions.php.

Grazie, dswebsme per aver postato una risposta così dettagliata. Dammi il conto in banca che ti offro una birra! [:

Saluti!

Dimitrov Dimitrov
6 ott 2015 09:42:33

Solo una piccola modifica, nell'ultima funzione in questa riga: $template = get_template_directory() . '/' . $post_meta['_wp_page_template'][0]; invece di usare get_template_directory(), usa get_stylesheet_directory(), es: $template = get_stylesheet_directory() . '/' . $post_meta['_wp_page_template'][0]; Sto usando un child theme e get_template_directory() recupererà la directory del tema genitore, quindi se metti il tuo template personalizzato nella posizione del child theme il server non lo troverà e darà un errore php, mentre get_stylesheet_directory() restituirà la directory corretta del template. Bel Codice Grazie

Salam El-Banna Salam El-Banna
4 ago 2016 09:38:46

Come posso usare questo per individuare i file template nella directory del plugin?

UncaughtTypeError UncaughtTypeError
21 giu 2018 12:25:38
0

So che questa è una vecchia domanda, ma volevo aggiungere la mia risposta su cosa mi ha aiutato ad applicare i template ai tipi di post personalizzati.

Ho creato il mio tipo di post personalizzato manualmente (con il mio plugin) invece di usare il plugin Custom Post Type UI.

Il codice del plugin è questo:

<?php
/**
 * Plugin Name: [Il tuo plugin per il tipo di post]
 */

defined('ABSPATH') or die(''); // Impede l'accesso al file tramite URL nel browser

function yourPostTypeFunction() {
    register_post_type('your-post-type', array(
            'labels'=>array(
                'name'=>__('Pagine del tipo di post'),
                'singular_name'=>__('Pagina del tipo di post')
            ),
            'description'=>'La tua descrizione',
            'public'=>true,
            'hierarchical'=>true, // Permette relazioni genitore-figlio
            'show_in_rest'=>true,
            'supports'=>array( // Caratteristiche supportate dal tipo di post
                'title',
                'editor',
                'thumbnail',
                'revisions',
                'page-attributes' /* Necessario per mostrare il metabox per impostare le relazioni genitore-figlio
se non stai usando il metodo del campo header 'Template Post Type' */
            ),
            'has_archive'=>true,
        )
    );
}
add_action('init', 'yourPostTypeFunction');

function CPTFlushPermalinks() {
    yourPostTypeFunction();
    flush_rewrite_rules();
}
register_activation_hook(__FILE__, 'CPTFlushPermalinks');

?>

Vedi qui per i dettagli su tutte le opzioni di register_post_type() (e perché c'è un flush_rewrite_rules() alla fine). Molte sono impostate come predefinite e non devi aggiungerle.

La cosa importante per me era che 'hierarchical' fosse impostato su true e che 'supports' includesse 'page-attributes'.

Dopo questo, ho creato il mio file template. Il codice è qualcosa del genere:

<?php
/*
 * Template Name: [Il tuo template]
 */

// ... contenuto della pagina (per lo più copiato da page.php)

?>

Nota come ho omesso il campo "Template Post Type" post-WordPress-4.7? Questo perché questo campo serve per permettere la visibilità di un template nel metabox 'Attributi pagina' a specifici tipi di post. I tipi di post elencati potranno scegliere il template nel loro metabox Attributi pagina. (Se fai così, non avrai bisogno del supporto 'page-attributes' nel tuo tipo di post - il metabox verrà creato automaticamente.) Senza di esso, solo i post di tipo 'page' possono scegliere diversi template nel loro metabox 'Attributi pagina'.

Non era quello che volevo, perché volevo che il mio template fosse applicato immediatamente a tutte le pagine con quel tipo di post. Questo si ottiene nominando il template "single-[nome tipo di post].php".

Ci sono altri schemi di nomi che puoi usare per altri scopi, elencati qui.

Questo funzionerà per le pagine singole del tipo di post. Non so per le sottosezioni, come chiedeva la domanda originale; lì probabilmente avrai bisogno di istruzioni condizionali nei tuoi altri template, come "if ('your-post-type' === get_post_type()) { ...".

Puoi rinominare liberamente il template personalizzato tramite il suo campo "Template Name" senza influenzare le pagine. Se vuoi rinominare il file, puoi farlo, ma poi devi andare nel tuo database, cercare il nome del file e rinominare le istanze con il nuovo nome.

Inoltre, il plugin Post Type Switcher è utile per cambiare i post tra tipi di post, anche in blocco. Ha funzionato perfettamente per me senza bug.

La risposta di dswebsme era una risposta appropriata del periodo prima che WordPress permettesse ai non-'pages' di scegliere template personalizzati nel box 'Attributi pagina'.

1 giu 2020 09:12:01
2

I tipi di post personalizzati possono avere template selezionabili, dovrai solo implementarli tu stesso. Il modo in cui WordPress lo gestisce internamente con il tipo di post "page" è salvare uno slug del template nei metadati del post, quindi verificare se esiste un valore lì quando il template viene caricato sul front end secondo la gerarchia.

Il processo base consisterebbe nell'aggiungere un meta box al tuo tipo di post personalizzato per permettere agli utenti di selezionare un template (magari utilizzando get_page_templates per creare una lista).

Il secondo passo è aggiungere un filtro a single_template per caricare il template selezionato quando quell'oggetto viene visualizzato sul front end.

Se guardi all'interno dei file core wp-includes/template.php e wp-includes/post-template.php, puoi vedere il codice che WordPress utilizza (e dove viene applicato il filtro) e adattarlo alle tue esigenze. get_queried_object ti fornirà il post_type dell'oggetto all'interno del filtro, oltre all'ID per permetterti di recuperare i metadati del post.

EDIT -

Ecco un esempio di filtro per il tipo di post post che carica qualsiasi cosa si trovi nel meta key my_template (come qualunque.php). Puoi testarlo creando un nuovo post e inserendo un nome file sotto quella chiave utilizzando il meta box nativo Campi Personalizzati. Puoi modificarlo per il tuo tipo personalizzato (cambiando 'post'), e qualsiasi schema tu stia usando per memorizzare e nominare i file.

function wpd_post_type_template( $template ){
    $object = get_queried_object();
    if( ! empty( $object->post_type )
        && 'post' == $object->post_type
        && $slug = get_post_meta( $object->ID, 'my_template', true ) ){
        if( $custom_template = locate_template( $slug, false ) ){
            $template = $custom_template;
        }
    }
    return $template;
}
add_filter( 'single_template', 'wpd_post_type_template' ) ;
5 ott 2015 20:38:58
Commenti

Questo inizialmente era il mio primo tentativo.

Ho implementato il meta box con un menu a tendina di tutti i template disponibili all'interno della cartella del mio tema. Ora cercherò informazioni con gli indizi che mi hai dato. Ma se hai qualche tutorial in mente, sentiti libero di condividerlo qui con me.

Grazie!

Dimitrov Dimitrov
5 ott 2015 20:54:56

@Dimitrov vedi la modifica sopra per un esempio di filtro.

Milo Milo
5 ott 2015 21:15:57