Aplicarea unui șablon pentru tipuri de postări personalizate
Astăzi am avut un client care dorea șabloane personalizate pentru fiecare pagină și secțiune din cadrul acestora. Am propus Laravel custom, dar clientul a dorit WordPress, deoarece pare mai ușor de întreținut (nu din experiența mea).
.. Până aici totul bine. Sunt puțin confuz în acest punct.
Așa că am decis că entitatea mea părinte va fi pagina, astfel încât să pot aplica șabloane diferite fiecărei pagini. Apoi am secțiuni în această pagină, care nu ar trebui să fie codate direct. Utilizatorul ar trebui să aibă posibilitatea de a alege aspectul secțiunii (șablonul), și să poată elimina sau reordona secțiunile în cadrul paginii curente. Și în final, am postările care sunt cele mai mici entități din site. Postările ar urma să fie afișate ca și coloane, ca în bootstrap (col-md-2, col-lg-6), etc..
Am decis să creez un tip de postare personalizat pentru a-l folosi ca secțiune, dar apoi am citit că tipurile de postări nu pot avea șabloane, doar paginile pot avea așa ceva. Acest lucru mi-a compromis planul până acum și am petrecut 5 ore căutând o soluție (fără succes). Așa că am nevoie de o altă strategie pentru a realiza acest lucru. Am nevoie de șabloane pentru două entități.
Poate cineva să sugereze o soluție pentru această problemă? (Vă voi cumpăra o bere!)
EDITARE:
Pentru a crea propriul tip de postare personalizat, folosesc un plugin în WordPress numit 'Custom Post Type UI', bineînțeles există și o altă modalitate, prin adăugarea unui scurt fragment de cod în fișierul functions.php, dar nu voi acoperi acest aspect aici.

Începând cu WordPress 4.7, tipurile personalizate de postări (Custom Post Types) au primit suport pentru mai multe șabloane.
Pentru a face un șablon disponibil pentru tipul tău personalizat de postare, adaugă acest antet în secțiunea meta a fișierului șablon:
Template Post Type: post, foo, bar
De exemplu, să presupunem că tipul tău personalizat de postare este etichetat "my_events" și dorești să faci un șablon numit "Fullwidth" disponibil atât pentru Pagini, cât și pentru tipul tău personalizat de postare.
Asta:
/**
* Template Name: Fullwidth
*
* Template Description...
**/
Devine asta:
/**
* Template Name: Fullwidth
* Template Post Type: page, my_events
*
* Template Description...
**/
Mai multe informații: Șabloane pentru tipuri de postări în 4.7 din WordPress Core

În mod normal nu aș urma un răspuns atât de detaliat ca cel al lui @Milo cu un post ca acesta. Dar, având în vedere că deja aveam acest cod scris pentru un alt proiect, am considerat că merită să îl împărtășesc.
Codul de mai jos face tot ceea ce @Milo a rezumat în răspunsul său și l-am utilizat deja în mai multe proiecte cu succes.
Iată un mic ghid pentru a înțelege ce se întâmplă:
1) Folosim acțiunea 'add_meta_boxes' pentru a adăuga o nouă casetă meta personalizată pe ecranul de editare (cu excepția tipului de postare nativ 'page').
2) Folosim acțiunea 'admin_menu' pentru a elimina caseta meta existentă 'Page Attributes' (cu excepția tipului de postare nativ 'page').
3) Construim caseta meta personalizată pentru a înlocui funcționalitatea casetei meta native 'Page Attributes'. Aceasta include câmpuri pentru definirea PĂRINTELUI, ȘABLONULUI și ORDINEA oricărui tip de postare personalizat pe care l-ai inițializat.
4) Folosim acțiunea 'save_post' pentru a salva selecția șablonului în metadatele postării.
5) Folosim filtrul 'single_template' pentru a încărca șablonul personalizat în locul șablonului implicit WordPress.
Iată codul funcțional pe care îl poți copia și lipi:
/** Selector de Șabloane pentru Tipuri Personalizate de Postări **/
function cpt_add_meta_boxes() {
$post_types = get_post_types();
foreach( $post_types as $ptype ) {
if ( $ptype !== 'page') {
add_meta_box( 'cpt-selector', 'Atribute', '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' => __('(fără părinte)'),
'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>Părinte</strong></p>";
echo "<label class=\"screen-reader-text\" for=\"parent_id\">Părinte</label>";
echo $pages;
}
}
// Selector de Șablon
echo "<p><strong>Șablon</strong></p>";
echo "<select id=\"cpt-selector\" name=\"_wp_page_template\"><option value=\"default\">Șablon Implicit</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>";
// Ordinea paginii
echo "<p><strong>Ordine</strong></p>";
echo "<p><label class=\"screen-reader-text\" for=\"menu_order\">Ordine</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' );
/** Sfârșit Selector de Șabloane pentru Tipuri Personalizate de Postări **/
Singura presupunere pe care am făcut-o este că șabloanele tale urmează o convenție de denumire bună, și anume:
tipdepostare-numesablon.php
De exemplu, poți defini niște șabloane personalizate pentru un tip de postare personalizat "Eveniment" folosind următoarea convenție de denumire în cadrul temei tale:
eveniment-standard.php
eveniment-toataziua.php
eveniment-repetabil.php
Acest cod este destul de inteligent pentru a permite aplicarea șabloanelor "eveniment" doar pentru tipul de postare Eveniment. Cu alte cuvinte, un șablon numit "sectiune-video.php" nu va fi vizibil pentru tipul de postare Eveniment. În schimb, acel șablon va apărea ca opțiune pentru tipul de postare "Secțiune".
Pentru a elimina această funcționalitate, trebuie doar să elimini logica condițională din codul de mai sus:
if ( $post->post_type == strstr( $template_filename, '-', true) ) { }

De dragul utilizării caracteristicii, marchez răspunsul tău ca 'Cel mai bun răspuns', pentru că explică foarte clar 'cum să aplici un șablon unui tip de postare personalizat'. Și bineînțeles, oferă un fragment de cod foarte util pe care îl pot copia și lipi direct ( visul devenit realitate ) în fișierul meu functions.php.
Mulțumesc, dswebsme pentru un răspuns atât de detaliat. Dă-mi contul bancar să-ți cumpăr o bere! [:
Noroc!

Doar o mică corectură, în ultima funcție la această linie: $template = get_template_directory() . '/' . $post_meta['_wp_page_template'][0]; în loc să folosești get_template_directory(), folosește get_stylesheet_directory(), ex: $template = get_stylesheet_directory() . '/' . $post_meta['_wp_page_template'][0]; Eu folosesc un child theme și get_template_directory() va returna directorul temei părinte, așa că dacă ai pus șablonul personalizat în child theme, serverul nu îl va găsi și va da eroare php, în timp ce get_stylesheet_directory() va returna directorul corect al șablonului. Cod grozav, mulțumesc

Știu că aceasta este o întrebare veche, dar am vrut să adaug răspunsul meu cu privire la ceea ce m-a ajutat să aplic șabloane la tipurile personalizate de postări.
Am creat tipul meu personalizat de postare manual (cu propriul meu plugin) în loc să folosesc pluginul Custom Post Type UI.
Codul pluginului arată astfel:
<?php
/**
* Plugin Name: [Pluginul tău pentru tipul de postare]
*/
defined('ABSPATH') or die(''); // Previne accesul la fișier prin URL în browser
function functiaTaPentruTipulDePostare() {
register_post_type('tipul-tau-de-postare', array(
'labels'=>array(
'name'=>__('Pagini de tipul de postare'),
'singular_name'=>__('Pagina de tipul de postare')
),
'description'=>'Descrierea ta',
'public'=>true,
'hierarchical'=>true, // Permite relații părinte-copil
'show_in_rest'=>true,
'supports'=>array( // Caracteristici pe care tipul de postare ar trebui să le suporte
'title',
'editor',
'thumbnail',
'revisions',
'page-attributes' /* Necesar pentru a afișa caseta meta pentru setarea relațiilor părinte-copil
dacă nu folosești metoda câmpului de antet 'Template Post Type' */
),
'has_archive'=>true,
)
);
}
add_action('init', 'functiaTaPentruTipulDePostare');
function reinitializarePermalinkuriCPT() {
functiaTaPentruTipulDePostare();
flush_rewrite_rules();
}
register_activation_hook(__FILE__, 'reinitializarePermalinkuriCPT');
?>
Vezi aici pentru detalii despre toate opțiunile pentru register_post_type()
(și de ce există un flush_rewrite_rules()
la sfârșit). Multe sunt setate ca implicite și nu trebuie adăugate.
Lucrul important pentru mine a fost că 'hierarchical'
a fost setat la true
și 'supports'
includea 'page-attributes'
.
După aceasta, am creat fișierul meu de șablon. Codul arată cam așa:
<?php
/*
* Template Name: [Șablonul tău]
*/
// ... conținutul paginii (copiat în mare parte din page.php)
?>
Ai observat că am omis câmpul post-WordPress-4.7 "Template Post Type"? Acesta este pentru a permite vizibilitatea unui șablon în caseta meta 'Atributele paginii' pentru anumite tipuri de postări. Tipurile de postări enumerate vor putea alege șablonul în caseta lor meta Atributele paginii. (Dacă faci asta, nu vei avea nevoie de suportul 'page-attributes'
în tipul tău de postare – caseta meta va fi creată automat.) Fără aceasta, doar postările de tipul 'page' pot alege diferite șabloane în caseta lor meta 'Atributele paginii'.
Nu asta am vrut eu, deoarece am dorit ca șablonul meu să fie aplicat imediat tuturor paginilor cu tipul de postare. Acest lucru se realizează prin denumirea șablonului "single-[numele tipului de postare].php".
Există și alte scheme de denumire pe care le poți folosi pentru alte scopuri, enumerate aici.
Aceasta va funcționa pentru paginile individuale ale tipului de postare. Nu știu despre subsecțiuni, cum a întrebat inițial; acolo, probabil ai nevoie de declarații condiționale în alte șabloane, cum ar fi "if ('tipul-tau-de-postare' === get_post_type()) { ...
".
Poți redenumi liber șablonul personalizat prin câmpul său "Template Name" fără a afecta paginile. Dacă vrei să redenumești fișierul, poți face acest lucru, dar trebuie să intri în baza ta de date, să cauți numele fișierului și să redenumești instanțele la noul nume.
De asemenea, pluginul Post Type Switcher este util pentru a comuta postările între tipuri de postări, inclusiv în masă. A funcționat fără probleme pentru mine, fără erori.
Răspunsul lui dswebsme a fost un răspuns adecvat dinainte ca WordPress să permită postărilor non-'page' să aleagă șabloane personalizate în caseta 'Atributele paginii'.

Tipurile personalizate de postări pot avea șabloane selectabile, doar că va trebui să le implementați singur. Modul în care WordPress face acest lucru intern cu tipul de postare "pagina" este prin salvarea unui slug de șablon în metadatele postării, apoi verificând dacă există o valoare acolo când șablonul este încărcat în partea de front-end conform ierarhiei.
Procesul de bază ar fi să adăugați o cutie meta la tipul dvs. de postare personalizată pentru a permite utilizatorilor să selecteze un șablon (poate folosind get_page_templates
pentru a construi o listă).
Al doilea pas este să adăugați un filtru la single_template
pentru a încărca șablonul selectat atunci când acel obiect este vizualizat în partea de front-end.
Dacă priviți în fișierele de bază wp-includes/template.php
și wp-includes/post-template.php
, puteți vedea codul pe care îl folosește WordPress (și unde este aplicat filtrul) și să-l adaptați la nevoile dvs. get_queried_object
vă va oferi post_type
al obiectului în cadrul filtrului, precum și ID-ul pentru a vă permite să preluați metadatele postării.
EDITARE -
Iată un exemplu de filtru pentru tipul de postare post
care încarcă orice se află în cheia meta my_template
(cum ar fi whatever.php
). Puteți testa acest lucru prin crearea unei noi postări și introducerea unui nume de fișier sub acea cheie folosind cutia meta Câmpuri Personalizate native. Puteți modifica acest lucru pentru tipul dvs. personalizat (schimbați 'post'
) și orice schemă folosiți pentru a stoca și denumi fișierele.
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' ) ;

Aceasta a fost prima mea încercare inițială.
Am implementat caseta meta cu meniul derulant care conține toate șabloanele disponibile din directorul temei mele. Acum voi cerceta pe baza indiciilor pe care mi le-ai oferit. Dar dacă ai vreun tutorial în minte, te rog să-l împărtășești aici cu mine.
Mulțumesc!
