single-{$post_type}-{slug}.php pentru tipuri personalizate de postări
Partea mea preferată din ierarhia de template-uri WordPress este posibilitatea de a crea rapid fișiere de template pentru pagini după slug, fără a fi nevoie să editezi pagina în WordPress pentru a selecta un template.
În prezent putem face asta:
page-{slug}.php
Dar aș dori să pot face acest lucru:
single-{post_type}-{slug}.php
Astfel încât, de exemplu, pentru un post type numit review
, să pot crea un template pentru un post numit "Recenzia mea minunată" în single-review-recenzia-mea-minunata.php
A mai configurat cineva acest lucru? single-{post_type}-{slug}.php

A) Baza în Nucleu
După cum puteți vedea în Codex explicația despre Ierarhia de Șabloane, single-{$post_type}.php
este deja suportat.
B) Extinderea Ierarhiei Nucleare
Acum există, din fericire, niște filtre și hook-uri în /wp-includes/template-loader.php
.
do_action('template_redirect');
apply_filters( 'template_include', $template )
- ȘI: un filtru specific în interiorul
get_query_template( $type, ... )
numit:"$type}_template"
B.1) Cum funcționează
- În fișierul încărcătorului de șabloane, șablonul este încărcat printr-o condițională de query var/wp_query:
is_*()
. - Condiționala declanșează apoi (în cazul unui șablon "single"):
is_single() && $template = get_single_template()
- Aceasta declanșează apoi
get_query_template( $type, $templates )
, unde$type
estesingle
- Apoi avem filtrul
"{$type}_template"
C) Soluția
Deoarece noi doar vrem să extindem ierarhia cu un șablon care este încărcat înainte de șablonul actual "single-{$object->post_type}.php"
, vom intercepta ierarhia și vom adăuga un nou șablon la începutul array-ului de șabloane.
// Extinde ierarhia
function add_posttype_slug_template( $templates )
{
$object = get_queried_object();
// Nou
$templates[] = "single-{$object->post_type}-{$object->post_name}.php";
// Ca în nucleu
$templates[] = "single-{$object->post_type}.php";
$templates[] = "single.php";
return locate_template( $templates );
}
// Acum adăugăm filtrul la hook-ul potrivit
function intercept_template_hierarchy()
{
add_filter( 'single_template', 'add_posttype_slug_template', 10, 1 );
}
add_action( 'template_redirect', 'intercept_template_hierarchy', 20 );
NOTĂ: (Dacă doriți să folosiți altceva decât slug-ul implicit al obiectului) Va trebui să ajustați $slug
în funcție de structura permalink-urilor. Pur și simplu folosiți orice aveți nevoie din globalul (object) $post
.
Tichete Trac
Deoarece abordarea de mai sus nu este în prezent suportată (puteți filtra doar calea absolută localizată în acest fel), iată o listă de tichete trac:
- Extinderea ierarhiei cu post_type->slug
- Introducerea unui filtru pentru
get_query_template()
- Filtrarea întregii ierarhii - cel mai promițător tichet ⤎ urmăriți acest tichet de @scribu ca cc

Vreau să testez asta, dar se pare că lipsește ceva în linia ta add_filter de la final.

@supertrue Bună observație. :) Am găsit un alt )
lipsă în interiorul filtrului. Am remediat. Poate ai vrea să înlocuiești liniuța cu o subliniere înainte de slug în interiorul șablonului. Doar pentru a face sufixul să iasă în evidență mai bine când te uiți peste șabloane.

Cauzează această eroare pe tot site-ul: Avertisment: array_unshift() [function.array-unshift]: Primul argument ar trebui să fie un array în [linia care conține array_unshift]

Ok, dar atunci altceva interceptează șabloanele de bază. Funcția funcționează corect și $templates
este un array. Vezi funcțiile de bază în acest pastebin (fără dată de expirare). Asigură-te că testezi asta cu o instalare fără plugin-uri și cu tema implicită. Apoi activează unul câte unul și vezi dacă eroarea mai apare.

Da, am depanat asta și am primit înapoi calea absolută finală a primului șablon găsit ca șir de caractere. Va trebui să vorbesc cu un dezvoltator de bază despre asta, înainte de a modifica răspunsul. De asemenea: am amestecat ceva: slug
este disponibil doar pentru termeni și taxonomii. Ar trebui să înlocuiești $post->post_name
cu ceea ce se potrivește cu structura ta de permalink-uri. În prezent, nu există nicio modalitate de a face asta automat pentru toate cazurile prin preluarea și înlocuirea căii în funcție de structura ta de permalink-uri și regulile de rescriere. Așteaptă o altă actualizare.

Ți-am lăsat o versiune funcțională - totuși, aceasta nu este satisfăcătoare (copierea/inserarea array-ului de bază și apoi adăugarea lui din nou într-o funcție personalizată).

acest lucru nu pare să aibă niciun efect... partea finală este corectă? Nu văd intercept_template_hierarchy
apelat nicăieri. Schimbarea ultimului add_posttype_slug_template
în intercept_template_hierarchy
a rezultat în Eroare fatală: operatorul [] nu este suportat pentru șiruri

@supertrue Da, aceasta este o problemă. Pur și simplu nu funcționează. Deja vorbesc cu un dezvoltator de bază pentru a include acest lucru în ciclul 3.4. Revino în câteva zile și readaugă această întrebare cu un comentariu aici. Mulțumesc. Notă laterală: Nucleul se comportă extrem de stupid în acest punct, deoarece array-ul de șabloane nu este filtrat, ci calea șablonului localizată în schimb. Consider că aceasta este de fapt o defect.

@supertrue Discuția a început deja. Așteaptă un update în zilele următoare. Dacă nu va fi un update, atunci poate un ticket trac :P

@supertrue Dominik Schilling (cunoscut și sub numele de Ocean90) s-a alăturat unora dintre tichete. Așteptați progrese în versiunea 3.4.

@kaiser soluția ta arată foarte bine, dar nu funcționează ($templates nu este un array în cazul meu… primesc eroare fatală). Există vreo șansă să o faci să funcționeze?

Urmând imaginea Ierarhiei de Șabloane, nu văd o astfel de opțiune.
Deci, iată cum aș proceda:
Soluția 1 (cea mai bună în opinia mea)
Crează un fișier de șablon și asociază-l cu recenzia
<?php
/*
Template Name: Recenzia mea grozavă
*/
?>
Adăugând fișierul php cu șablon în directorul temei tale, acesta va apărea ca o opțiune de șablon în pagina de editare a articolului.
Soluția 2
Aceasta ar putea fi probabil realizată folosind hook-ul template_redirect
.
În fișierul 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' );
EDITARE
Am adăugat verificarea file_exists

@kaiser Probabil a fost în tutorialul pe care l-am urmat la vremea respectivă, dacă nu este necesar îl voi elimina.

@kaiser: exit()
este necesar pentru a preveni încărcarea șablonului implicit.

Cel mai bun răspuns (de acum 4 ani) nu mai funcționează, dar codex-ul WordPress are soluția aici:
<?php
/**
* Adaugă un template personalizat bazat pe slug-ul postului și tipul de post
*/
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 );
?>

În cazul meu, am tipuri de postări personalizate Album și Track legate printr-o taxonomie Album. Am dorit să pot folosi șabloane Single diferite pentru postările Album și Track în funcție de taxonomia lor Album.
Bazat pe răspunsul lui Kaiser de mai sus, am scris acest cod. Funcționează bine.
Notă. Nu am avut nevoie de add_action().
// Adaugă o opțiune suplimentară de șablon în ierarhia de șabloane
add_filter( 'single_template', 'add_albumtrack_taxslug_template', 10, 1 );
function add_albumtrack_taxslug_template( $orig_template_path )
{
// în acest moment, $orig_template_path este o cale absolută către șablonul single preferat.
$object = get_queried_object();
if ( ! (
// specifică o altă opțiune de șablon doar pentru tipurile de postări Album și Track.
in_array( $object->post_type, array( 'gregory-cpt-album','gregory-cpt-track' )) &&
// verifică dacă taxonomia Album a fost înregistrată.
taxonomy_exists( 'gregory-tax-album' ) &&
// obține termenul taxonomiei Album pentru postul curent.
$album_tax = wp_get_object_terms( $object->ID, 'gregory-tax-album' )
))
return $orig_template_path;
// asamblează numele șablonului
// presupunere: doar un termen de taxonomie Album per post. folosim primul obiect din array.
$template = "single-{$object->post_type}-{$album_tax[0]->slug}.php";
$template = locate_template( $template );
return ( !empty( $template ) ? $template : $orig_template_path );
}
Acum pot crea șabloane numite single-gregory-cpt-track-tax-serendipity.php și single-gregory-cpt-album-tax-serendipity.php iar WP le va folosi automat; 'tax-serendipity' este slug-ul pentru primul termen al taxonomiei Album.
pentru referință, cârligul de filtrare 'single_template' este declarat în:
/wp-includes/theme.php: get_query_template()
Mulțumesc lui Kaiser pentru codul exemplu.
Toate cele bune, Gregory

Salut Greg - bun venit pe WPSE. Te rog să postezi răspunsuri doar ca răspunsuri la întrebări - nu ca întrebări suplimentare. Dacă ai o întrebare care nu este răspunsă de un răspuns existent și este prea complexă pentru un comentariu, te rog să deschizi o altă întrebare :)

funcționează pentru tine? în primul rând, '$template' nu ar trebui să fie comentat în codul tău.. și cred că în loc de '$album_tax[0]->slug' ar trebui să fie '$object->post_name', nu-i așa?

Folosește șabloane de pagini
O altă abordare pentru scalabilitate ar fi să duplicăm funcționalitatea dropdown-ului de șabloane de pagini de pe tipul de postare page
pentru tipul tău de postare personalizat.
Cod Reutilizabil
Duplicarea în cod nu este o practică bună. În timp, poate cauza o umflare severă a unei baze de cod, ceea ce poate face foarte dificil pentru un dezvoltator să o gestioneze. În loc să creezi un șablon pentru fiecare slug individual, cel mai probabil vei avea nevoie de un șablon one-to-many care poate fi reutilizat, în loc de unul one-to-one post-to-șablon.
Codul
# Definește șirul tipului tău de postare personalizat
define('MY_CUSTOM_POST_TYPE', 'my-cpt');
/**
* Înregistrează meta box-ul
*/
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', #Prefer plasarea sub meta box-ul acțiunilor postării
'low'
);
}
/**
* Randarea meta box-ului - Acest cod este similar cu cel randat pe tipul de postare 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'>Șablon Pagină</label>
<select name='_wp_page_template' id='page_template'>
<option value='default'>Șablon Implicit</option>";
page_template_dropdown($template);
echo "</select>";
}
/**
* Salvează șablonul paginii
* @return void
*/
function save_page_template($post_id){
# Sari peste salvările automate
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return;
elseif ( defined( 'DOING_AJAX' ) && DOING_AJAX )
return;
elseif ( defined( 'DOING_CRON' ) && DOING_CRON )
return;
# Actualizează meta-ul șablonului paginii doar dacă suntem pe tipul nostru specific de postare
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');
/**
* Setează șablonul paginii
* @param string $template Șablonul determinat de WordPress
* @return string $template Calea completă către șablonul predefinit sau personalizat al paginii
*/
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)
#deoarece dropdown-ul nostru oferă doar numele de bază, folosește funcția locate_template() pentru a găsi cu ușurință calea completă
return locate_template($custom_template);
}
return $template;
}
add_filter('single_template', 'set_page_template');
Acesta este un răspuns puțin târziu, dar am considerat că ar fi valoros deoarece nimeni pe internet nu a documentat această abordare, din câte știu. Sper că acest lucru îi va ajuta pe cineva.

Actualizare pentru codul lui Brian, am descoperit că atunci când caseta derulantă nu era utilizată, opțiunea "default" pentru șablon era salvată în wp_page_template, ceea ce determina sistemul să încerce să găsească un șablon numit default. Această modificare verifică doar dacă opțiunea este "default" la salvare și șterge meta-ul postului în schimb (util dacă ai schimbat opțiunea de șablon înapoi la 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;
}
