Elimină slug-ul taxonomiei dintr-un permalink de taxonomie ierarhică personalizată
Am creat o taxonomie 'forum', folosind aceste reguli:
register_taxonomy(
'forum',
array('topic'),
array(
'public' => true,
'name' => _a('Forumuri'),
'singular_name' => _a('Forum'),
'show_ui' => true,
'show_in_nav_menus' => true,
'hierarchical' => true,
'labels' => array(
'name' => _a('Forumuri'),
'singular_name' => _a('Forum'),
'search_items' => _a('Caută Forumuri'),
'popular_items' => _a('Forumuri Populare'),
'all_items' => _a('Toate Forumurile'),
'parent_item' => _a('Forum Părinte'),
'parent_item_colon' => _a('Forum Părinte:'),
'edit_item' => _a('Editează Forum'),
'update_item' => _a('Actualizează Forum'),
'add_new_item' => _a('Adaugă Forum Nou'),
'new_item_name' => _a('Nume Forum Nou'),
),
'query_var' => true,
'rewrite' => array('slug' => 'forums', 'with_front' => false, 'hierarchical' => true),
)
);
În frontend, URL-urile arată astfel:
forums/general-discussion/sub-forum
Cum pot elimina slug-ul din față ("forums")? Adică, să schimb URL-urile în:
general-discussion/sub-forum
Dacă trimit un argument slug gol către register_taxonomy() funcționează, dar acest lucru cauzează probleme cu permalink-urile tipului de postare asociat cu această taxonomie

ACTUALIZARE
De la scrierea acestui articol, WordPress Core a adăugat hook-ul 'do_parse_request'
care permite gestionarea elegantă a rutării URL-urilor fără a fi nevoie să extindem clasa WP
. Am acoperit acest subiect în profunzime în discursul meu de la WordCamp Atlanta 2014 intitulat "Hardcore URL Routing" (Rutarea Avansată a URL-urilor); slide-urile sunt disponibile la link.
RĂSPUNS ORIGINAL
Designul URL-urilor a fost important pentru mine timp de peste un deceniu; chiar am scris un blog despre asta acum câțiva ani. Și deși WordPress este, în ansamblu, un software genial, din păcate sistemul său de rescriere a URL-urilor lasă de dorit (după părerea mea, desigur. :) Oricum, mă bucur să văd oameni care se preocupă de designul URL-urilor!
Răspunsul pe care îl voi oferi este un plugin pe care îl numesc WP_Extended
, o dovadă de concept pentru această propunere pe Trac (Notă: propunerea a început ca una și a evoluat în altceva, așa că trebuie să o citești pe toată pentru a vedea unde a ajuns.)
În esență, ideea este să creăm o subclasă a clasei WP
, să suprascriem metoda parse_request()
și apoi să atribuim variabilei globale $wp
o instanță a subclasei. Apoi, în parse_request()
, inspectăm calea pe segmente în loc să folosim o listă de expresii regulate care trebuie să se potrivească cu întregul URL.
Pentru a fi mai explicit, această tehnică inserează logică în fața lui parse_request()
care verifică potriviri URL-RegEx și caută mai întâi termeni de taxonomie, dar NUMAI înlocuiește parse_request()
, lăsând intact restul sistemului de rutare WordPress, inclusiv și mai ales folosirea variabilei $query_vars
.
Pentru cazul tău, această implementare doar compară segmentele URL-ului cu termenii de taxonomie, deoarece asta este tot ce ai nevoie. Această implementare verifică termenii de taxonomie, respectând relațiile părinte-copil, iar când găsește o potrivire, atribuie calea URL-ului (fără slash-uri la început și sfârșit) la $wp->query_vars['category_name']
, $wp->query_vars['tag']
sau $wp->query_vars['taxonomy']
& $wp->query_vars['term']
, ocolind metoda parse_request()
a clasei WP
.
Pe de altă parte, dacă calea URL-ului nu se potrivește cu un termen din taxonomiile specificate, delegă logica de rutare către sistemul de rescriere WordPress prin apelarea metodei parse_request()
a clasei WP
.
Pentru a folosi WP_Extended
în cazul tău, va trebui să apelezi funcția register_url_route()
din fișierul functions.php
al temei tale, astfel:
add_action('init','init_forum_url_route');
function init_forum_url_route() {
register_url_route(array('taxonomy'=>'forum'));
}
Iată codul sursă pentru plugin:
<?php
/*
Filename: wp-extended.php
Plugin Name: WP Extended for Taxonomy URL Routes
Author: Mike Schinkel
*/
function register_url_route($args=array()) {
if (isset($args['taxonomy']))
WP_Extended::register_taxonomy_url($args['taxonomy']);
}
class WP_Extended extends WP {
static $taxonomies = array();
static function on_load() {
add_action('setup_theme',array(__CLASS__,'setup_theme'));
}
static function register_taxonomy_url($taxonomy) {
self::$taxonomies[$taxonomy] = get_taxonomy($taxonomy);
}
static function setup_theme() { // Setup theme is 1st code run after WP is created.
global $wp;
$wp = new WP_Extended(); // Replace the global $wp
}
function parse_request($extra_query_vars = '') {
$path = $_SERVER['REQUEST_URI'];
$domain = str_replace('.','\.',$_SERVER['SERVER_NAME']);
//$root_path = preg_replace("#^https?://{$domain}(/.*)$#",'$1',WP_SITEURL);
$root_path = $_SERVER['HTTP_HOST'];
if (substr($path,0,strlen($root_path))==$root_path)
$path = substr($path,strlen($root_path));
list($path) = explode('?',$path);
$path_segments = explode('/',trim($path,'/'));
$taxonomy_term = array();
$parent_id = 0;
foreach(self::$taxonomies as $taxonomy_slug => $taxonomy) {
$terms = get_terms($taxonomy_slug);
foreach($path_segments as $segment_index => $path_segment) {
foreach($terms as $term_index => $term) {
if ($term->slug==$path_segments[$segment_index]) {
if ($term->parent!=$parent_id) { // Make sure we test parents
$taxonomy_term = array();
} else {
$parent_id = $term->term_id; // Capture parent ID for verification
$taxonomy_term[] = $term->slug; // Collect slug as path segment
unset($terms[$term_index]); // No need to scan it again
}
break;
}
}
}
if (count($taxonomy_term))
break;
}
if (count($taxonomy_term)) {
$path = implode('/',$taxonomy_term);
switch ($taxonomy_slug) {
case 'category':
$this->query_vars['category_name'] = $path;
break;
case 'post_tag':
$this->query_vars['tag'] = $path;
break;
default:
$this->query_vars['taxonomy'] = $taxonomy_slug;
$this->query_vars['term'] = $path;
break;
}
} else {
parent::parse_request($extra_query_vars); // Delegate to WP class
}
}
}
WP_Extended::on_load();
P.S. ATENȚIE #1
Deși pentru un anumit site această tehnică funcționează excelent, această tehnică NU ar trebui NICIODATĂ folosită într-un plugin distribuit pe WordPress.org pentru alții. Dacă este la baza unui pachet software bazat pe WordPress, atunci ar putea fi OK. Altfel, această tehnică ar trebui limitată la îmbunătățirea rutării URL-urilor pentru un anumit site.
De ce? Pentru că doar un plugin poate folosi această tehnică. Dacă două plugin-uri încearcă să o folosească, vor intra în conflict.
Ca o paranteză, această strategie poate fi extinsă pentru a gestiona generic aproape orice model de utilizare necesar, și asta intenționez să implementez când voi găsi timpul liber sau un client care să sponsorizeze timpul necesar pentru o implementare complet generică.
ATENȚIE #2
Am scris acest cod pentru a suprascrie parse_request()
, care este o funcție foarte mare, și este posibil să fi omis câteva proprietăți ale obiectului global $wp
care ar fi trebuit setate. Dacă ceva nu funcționează cum trebuie, anunță-mă și voi investiga și voi reviziona răspunsul dacă este necesar.
Oricum...

După ce am scris acest lucru, mi-am dat seama că am testat pentru categorii și nu pentru termeni de taxonomie în general, așa că cele de mai sus nu vor funcționa pentru taxonomia 'forum'
, dar voi revizui codul să funcționeze mai târziu astăzi...

Deci, am actualizat codul pentru a rezolva problema pe care am menționat-o în comentariul anterior.

nu reușesc să fac asta să funcționeze... trebuie să modific regulile de rescriere?

@One Trick Pony - Puțin mai multe informații de diagnostic ar fi de ajutor. :) Ce ai încercat? Ce se întâmplă când introduci URL-urile în browser? Poate ai numit taxonomia ta 'forums'
în loc de 'forum'
? Te aștepți ca URL-urile care leagă aceste pagini să se schimbe (dacă da, nu e de mirare, codul meu nu se ocupă de generarea URL-urilor, ci doar de rutarea acestora.)

nu, pot schimba URL-urile (cred că funcția term_link este cea în care trebuie să fac hook pentru asta). site/rootforum/
funcționează, dar site/rootforum/subforum/
nu merge (eroare 404) ...

@One Trick Pony - Este subforum
un termen de taxonomie copil al lui rootforum
? Te rog să oferi mai multe detalii despre configurația ta actuală; este foarte greu să îți oferim o soluție care funcționează când nu suntem siguri cum arată exact configurația ta.

nu, este aceeași taxonomie. subforum
este un termen copil al rootforum

@One Trick Pony - Ce se întâmplă când "nu funcționează?" Care (toate) URL-uri nu funcționează? Care este domeniul real unde acest lucru nu funcționează? URL-ul pentru /forums/foo/bar
funcționează, dar /foo/bar
nu? Dă-mi mai multe informații și voi rezolva problema, dar acum simt că nu am suficiente detalii pentru a depana, deoarece la mine funcționează perfect.

vezi - http://dev.digitalnature.ro/wp99420/forum1/ - apoi încearcă să accesezi http://dev.digitalnature.ro/wp99420/forum1/general-discussion/ (general-discussion
este un sub-forum al forum1
)

dacă este relevant, iată întregul cod pentru sistemul de forum: http://pastebin.com/N509zCh3

@One Trick Pony - Ce este wp99420? Este acesta rădăcina site-ului? Codul meu nu gestionează în prezent localizarea rădăcinii unui site într-un subdirector; este ceea ce ai nevoie?

@One Trick Pony - Am actualizat codul pentru a gestiona site-urile într-un subdomeniu.

nu funcționează cu adevărat, încă primesc eroarea 404, dar oricum mulțumesc. Tocmai am aflat despre pluginul bbpress, așa că nu voi mai folosi acest lucru

@One Trick Pony - Chiar aș vrea să aflăm de ce nu funcționează pentru tine. Vreau să transform acest lucru într-un plugin pe care oricine îl poate folosi pentru orice URL-uri dorește, așa că aflarea motivului pentru care nu funcționează pentru tine ar fi de ajutor.

testând pe localhost/wp
primesc acest lucru ca $path_segments: Array ( [0] => wp [1] => rootforum [2] => subforum )
. $taxonomy_term și $taxonomy_slug sunt de asemenea ok, deci parse_request() pare să-și facă treaba. Poate problema este parent::parse_request() care resetează array-ul query_vars (vezi linia 123 în class-wp)?

@One Trick Pony - Care este valoarea ta pentru WP_SITEURL
? Returnează $_SERVER['SERVER_NAME']
valoarea 'localhost'
? Eu obișnuiesc să configurez domenii "locale" pentru testare (de ex. http://mytest.dev
în loc de http://localhost/wp
), așa că deseori nu prind erorile care apar în configurații non-standard.

@One Trick Pony - Am actualizat codul să nu mai depindă de WP_SITEURL; poți încerca din nou?

la fel... Oricum, am găsit problema, cel puțin pe sistemul meu - este șirul implodat $path
pe care îl transmiți la query_vars['term']. Arată ca rootforum/subform
(ar trebui să fie doar ultima parte, subforum
)

@One Trick Pony - rootforum
nu este un termen părinte 'subforum'
al taxonomiei 'forum'
?

rootforum este un termen, iar subforum este termenul copil al rootforum. ambele fac parte din taxonomia forum

@MikeSchinkel: chiar sper să ajungă acel ticket trac în nucleu. Mi se pare o idee excelentă (cu înțelegerea mea limitată). Apropo, ai acest plugin în depozitul WP?

Hei @MikeSchinkel, a avansat cumva acest lucru? Ideile tale sunt minunate și abordează una dintre cele mai mari probleme ale mele cu WP. S-a materializat vreodată acel plugin?

@PeterB - Mulțumesc pentru cuvintele frumoase. Încă lucrez la un plugin pentru uz general, dar am pregătit o prezentare detaliată la WordCamp despre Rutarea URL-urilor, slide-urile sunt aici: http://hardcorewp.com/2014/hardcore-url-routing-for-wordpress/

Simplu, chiar.
Pasul 1: Nu mai folosi deloc parametrul rewrite. Vom crea propriile reguli de rescriere.
'rewrite'=>false;
Pasul 2: Setați reguli de pagină verbose. Acest lucru forțează paginile normale să aibă propriile reguli în loc să fie o captură universală în partea de jos a paginii.
Pasul 3: Creați câteva reguli de rescriere pentru a gestiona cazurile de utilizare.
Pasul 4: Forțați manual o reîmprospătare a regulilor. Cea mai ușoară metodă: mergeți la setări->legături permanente și faceți clic pe butonul de salvare. Prefer această metodă în locul unei activări prin plugin pentru uzul meu personal, deoarece pot forța reîmprospătarea regulilor ori de câte ori modific ceva.
Deci, timpul pentru cod:
function test_init() {
// creăm o nouă taxonomie
register_taxonomy(
'forum',
'post',
array(
'query_var' => true,
'public'=>true,
'label'=>'Forum',
'rewrite' => false,
)
);
// forțăm regulile verbose.. acest lucru face ca fiecare Pagină să aibă propria regulă în loc să fie o
// captură universală, pe care o vom folosi pentru taxonomia forumului
global $wp_rewrite;
$wp_rewrite->use_verbose_page_rules = true;
// două reguli pentru gestionarea feed-urilor
add_rewrite_rule('(.+)/feed/(feed|rdf|rss|rss2|atom)/?$','index.php?forum=$matches[1]&feed=$matches[2]');
add_rewrite_rule('(.+)/(feed|rdf|rss|rss2|atom)/?$','index.php?forum=$matches[1]&feed=$matches[2]');
// o regulă pentru gestionarea paginării postărilor în taxonomie
add_rewrite_rule('(.+)/page/?([0-9]{1,})/?$','index.php?forum=$matches[1]&paged=$matches[2]');
// o regulă pentru afișarea normale a taxonomiei forum
add_rewrite_rule('(.+)/?$', 'index.php?forum=$matches[1]');
}
add_action( 'init', 'test_init' );
Amintiți-vă că după adăugarea acestui cod, trebuie să îl aveți activ când reîmprospătați regulile de legături permanente (prin Salvarea paginii la Setări->Legături permanente)!
După ce ați reîmprospătat regulile și le-ați salvat în baza de date, atunci /orice ar trebui să meargă către pagina taxonomiei forum=orice.
Regulile de rescriere nu sunt chiar atât de dificile dacă înțelegeți expresiile regulate. Folosesc acest cod pentru a mă ajuta în depanarea lor:
function test_foot() {
global $wp_rewrite;
echo '<pre>';
var_dump($wp_rewrite->rules);
echo '</pre>';
}
add_action('wp_footer','test_foot');
În acest fel, pot vedea regulile curente dintr-o privire pe pagina mea. Amintiți-vă doar că, pentru orice URL, sistemul începe de la partea de sus a regulilor și coboară până găsește una care se potrivește. Potrivirea este apoi folosită pentru a rescrie interogarea într-un set mai normal de tip ?cheie=valoare. Acele chei sunt analizate în ceea ce intră în obiectul WP_Query. Simplu.
Editare: Notă laterală, această metodă va funcționa probabil doar dacă structura normală a postărilor personalizate începe cu ceva care nu este o captură universală, cum ar fi %category% sau ceva similar. Trebuie să înceapă cu un șir static sau numeric, cum ar fi %year%. Acest lucru este necesar pentru a preveni capturarea URL-ului înainte de a ajunge la regulile dumneavoastră.

Dacă dorești un debug mai ușor al regulilor tale de rewrite, îți recomand (din nou) pluginul meu de analiză rewrite, care îți permite să testezi regulile și să vezi variabilele de interogare în timp real.

Din păcate, sistemul actual de URL rewrite forțează aplatizarea tuturor modelelor potențiale de URL într-o listă mare, în loc să urmeze structura arborescentă inerentă a căilor URL. Configurația actuală nu poate potrivi în mod convenabil un array de literali cum ar fi categorii sau nume de forum; după cum știi, forțează ca toate URL-urile "Page" să fie evaluate primele. Potrivirea pe segmente de cale și potrivirea în mai multe moduri (array de literali, categorii, tag-uri, termeni de taxonomie, nume de utilizator, tipuri de postări, nume de postări, funcții callback, hook-uri de filtrare și în final RegEx) ar scala mai bine pentru complexitate și ar fi mai ușor de înțeles.

Mike: De fapt, asta nu e deloc mai ușor de înțeles, pentru că n-am nici cea mai mică idee despre ce naiba vorbești acolo. Ideile tale despre rutarea URL-urilor sunt confuze și dificile, și după cum probabil știi, nu sunt de acord cu ele. Căutarea plată are mai mult sens și este mai flexibilă decât tind să-i recunoști. Majoritatea oamenilor nu doresc toată acea complexitate inutilă în URL-urile lor, și aproape nimeni nu are nevoie de ea.

Mulțumesc, dar cred că am încercat deja acest lucru înainte (http://wordpress.stackexchange.com/questions/9455/custom-post-type-permalinks-giving-404s)

Din fericire, WordPress Answers acum permite persoanelor care doresc controlul asupra URL-urilor lor să aibă în sfârșit o voce, și se pare că sunt mulți (100+). Dar respect faptul că poate nu poți să urmezi exemplul meu înainte de o implementare completă. Prezic că, odată ce abordarea pe care o susțin va fi implementată complet într-un plugin și după aproximativ 6-12 luni, aceasta va deveni metoda preferată pentru site-urile CMS bazate pe WordPress să își direcționeze URL-urile. Așadar, să reluăm această dezbatere în aproximativ 9 luni.

Nu veți putea face acest lucru folosind doar WP_Rewrite, deoarece acesta nu poate distinge între slug-urile de termeni și slug-urile de articole.
Trebuie să conectați și la hook-ul 'request' și să preveniți eroarea 404, setând variabila de interogare pentru articol în loc de cea pentru taxonomie.
Ceva de genul acesta:
function fix_post_request( $request ) {
$tax_qv = 'forum';
$cpt_name = 'post';
if ( !empty( $request[ $tax_qv ] ) ) {
$slug = basename( $request[ $tax_qv ] );
// dacă acest lucru ar genera o eroare 404
if ( !get_term_by( 'slug', $slug, $tax_qv ) ) {
// setează variabilele de interogare corecte
$request[ 'name' ] = $slug;
$request[ 'post_type' ] = $cpt_name;
unset( $request[$tax_qv] );
}
}
return $request;
}
add_filter( 'request', 'fix_post_request' );
Rețineți că taxonomia trebuie definită înainte de tipul de articol.
Acesta ar fi un moment bun pentru a sublinia că a avea o taxonomie și un tip de articol cu aceeași variabilă de interogare este o Idee Proastă.
De asemenea, nu veți putea accesa articolele care au același slug ca unul dintre termeni.

Sunt de acord că a avea o taxonomie și un tip de postare cu același query var este o Idee Proastă, dar asta ar putea sugera oamenilor că a avea o taxonomie și un tip de postare cu același nume este o idee proastă, ceea ce nu este cazul. Dacă folosiți același nume, atunci doar una dintre cele două ar trebui să aibă un query var.

Aș arunca o privire la codul pluginului pentru categorii de nivel superior:
http://fortes.com/projects/wordpress/top-level-cats/
Ai putea adapta ușor acest cod pentru a căuta slug-ul taxonomiei personalizate prin modificarea
$category_base = get_option('category_base');
de la linia 74 în ceva de genul:
$category_base = 'forums';

Sugerez să arunci o privire la plugin-ul Custom Post Permalinks. Nu am timp să testez acum, dar ar putea fi util în situația ta.

Deoarece sunt familiarizat cu cealaltă întrebare a ta, voi răspunde având în vedere acest lucru.
Nu am testat acest lucru deloc, dar ar putea funcționa dacă executați acest cod o singură dată imediat după ce înregistrați toate structurile de permalink-uri dorite:
class RRSwitcher {
var $rules;
function RRSwitcher(){
add_filter( 'topic_rewrite_rules', array( $this, 'topics' ) );
add_filter( 'rewrite_rules_array', array( $this, 'rules' ) );
}
function topics( $array ){
$this->rules = $array;
return array();
}
function rules( $array ){
return array_merge( (array)$array, (array)$this->rules );
}
}
$RRSwitcher = new RRSwitcher();
global $wp_rewrite;
$wp_rewrite->use_verbose_rules = true;
$wp_rewrite->flush_rules();
Ce face acest cod: elimină regulile de rescriere generate din permalink-ul subiectelor din fluxul normal al array-ului de reguli și le recombina la sfârșitul array-ului. Acest lucru previne interferența acestor reguli cu orice alte reguli de rescriere. Apoi, forțează regulile verbose de rescriere (fiecare pagină primește o regulă individuală cu o expresie regulată specifică). Acest lucru previne interferența paginilor cu regulile subiectelor tale. În final, execută o resetare completă (asigurați-vă că fișierul .htaccess este scriabil, altfel acest lucru nu va funcționa) și salvează array-ul foarte mare și foarte complicat de reguli de rescriere.

Există un plugin pentru aceasta.
Acesta elimină slug-ul de tip prin adăugarea unei reguli specifice pentru fiecare pagină de tip post personalizat.

Nu sunt sigur dacă va funcționa și pentru taxonomii, dar a funcționat pentru tipurile personalizate de postări
Deși nu a fost actualizat de 2 ani, următorul plugin a funcționat pentru mine: http://wordpress.org/plugins/remove-slug-from-custom-post-type/
Pentru informare, rulez WP 3.9.1
cu WP Types 1.5.7
