Rimuovere lo slug della tassonomia da un permalink di tassonomia gerarchica personalizzata
Ho creato una tassonomia 'forum', usando queste regole:
register_taxonomy(
'forum',
array('topic'),
array(
'public' => true,
'name' => _a('Forum'),
'singular_name' => _a('Forum'),
'show_ui' => true,
'show_in_nav_menus' => true,
'hierarchical' => true,
'labels' => array(
'name' => _a('Forum'),
'singular_name' => _a('Forum'),
'search_items' => _a('Cerca Forum'),
'popular_items' => _a('Forum Popolari'),
'all_items' => _a('Tutti i Forum'),
'parent_item' => _a('Forum Genitore'),
'parent_item_colon' => _a('Forum Genitore:'),
'edit_item' => _a('Modifica Forum'),
'update_item' => _a('Aggiorna Forum'),
'add_new_item' => _a('Aggiungi Nuovo Forum'),
'new_item_name' => _a('Nuovo Nome Forum'),
),
'query_var' => true,
'rewrite' => array('slug' => 'forums', 'with_front' => false, 'hierarchical' => true),
)
);
Nel front-end gli URL appaiono così:
forums/general-discussion/sub-forum
Come posso rimuovere lo slug iniziale ("forums")? Cioè, modificare gli URL in:
general-discussion/sub-forum
Se passo un argomento slug vuoto a register_taxonomy() funziona, ma questo causa problemi con i permalink del tipo di post associato a questa tassonomia

AGGIORNAMENTO
Dopo aver scritto questo articolo, il core di WordPress ha aggiunto l'hook 'do_parse_request'
che permette di gestire il routing degli URL in modo elegante senza dover estendere la classe WP
. Ho approfondito l'argomento nel mio intervento al WordCamp Atlanta 2014 intitolato "Hardcore URL Routing"; le slide sono disponibili al link.
RISPOSTA ORIGINALE
Il design degli URL è stato importante per me per oltre un decennio; anni fa ho persino scritto un blog al riguardo. E sebbene WordPress nel complesso sia un software brillante, purtroppo il suo sistema di riscrittura degli URL è quasi del tutto inefficace (a mio modesto parere, ovviamente. :) Comunque, sono contento di vedere persone interessate al design degli URL!
La risposta che fornirò è un plugin che chiamo WP_Extended
, una prova di concetto per questa proposta su Trac (Nota: la proposta è iniziata come una cosa ed è evoluta in un'altra, quindi bisogna leggerla tutta per capire dove stava andando.)
L'idea di base è sottoclasse della classe WP
, sovrascrivere il metodo parse_request()
, e poi assegnare la variabile globale $wp
con un'istanza della sottoclasse. All'interno di parse_request()
si ispeziona il percorso per segmenti invece di usare una lista di espressioni regolari che devono corrispondere all'URL nella sua interezza.
Quindi, per essere espliciti, questa tecnica inserisce una logica prima di parse_request()
che verifica le corrispondenze URL-RegEx e invece cerca prima corrispondenze con i termini tassonomici, ma SOLO sostituisce parse_request()
e lascia intatto tutto il resto del sistema di routing degli URL di WordPress, incluso e soprattutto l'uso della variabile $query_vars
.
Per il tuo caso d'uso, questa implementazione confronta solo i segmenti dell'URL con i termini tassonomici poiché è tutto ciò di cui hai bisogno. Questa implementazione ispeziona i termini tassonomici rispettando le relazioni genitore-figlio e quando trova una corrispondenza assegna il percorso dell'URL (meno gli slash iniziali e finali) a $wp->query_vars['category_name']
, $wp->query_vars['tag']
o $wp->query_vars['taxonomy']
& $wp->query_vars['term']
e bypassa il metodo parse_request()
della classe WP
.
D'altra parte, se il percorso dell'URL non corrisponde a un termine delle tassonomie specificate, delega la logica di routing al sistema di riscrittura di WordPress chiamando il metodo parse_request()
della classe WP
.
Per usare WP_Extended
per il tuo caso d'uso, dovrai chiamare la funzione register_url_route()
dal file functions.php
del tuo tema in questo modo:
add_action('init','init_forum_url_route');
function init_forum_url_route() {
register_url_route(array('taxonomy'=>'forum'));
}
Ecco il codice sorgente del 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 è il primo codice eseguito dopo la creazione di WP.
global $wp;
$wp = new WP_Extended(); // Sostituisce la variabile globale $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) { // Verifica i genitori
$taxonomy_term = array();
} else {
$parent_id = $term->term_id; // Salva l'ID del genitore per verifica
$taxonomy_term[] = $term->slug; // Aggiunge lo slug come segmento
unset($terms[$term_index]); // Non serve scansionarlo di nuovo
}
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); // Delega alla classe WP
}
}
}
WP_Extended::on_load();
P.S. AVVERTENZA #1
Sebbene per un sito specifico questa tecnica funzioni alla grande, questa tecnica NON dovrebbe MAI essere usata per un plugin da distribuire su WordPress.org per l'uso da parte di altri. Se è al centro di un pacchetto software basato su WordPress, allora potrebbe andar bene. Altrimenti, questa tecnica dovrebbe essere limitata a migliorare il routing degli URL per un sito specifico.
Perché? Perché solo un plugin può usare questa tecnica. Se due plugin provano a usarla, entreranno in conflitto.
Inoltre, questa strategia può essere espansa per gestire praticamente ogni modello d'uso richiesto, ed è ciò che intendo implementare non appena troverò il tempo libero o un cliente disposto a sponsorizzare il lavoro necessario per una implementazione completamente generica.
AVVERTENZA #2
Ho scritto questo per sovrascrivere parse_request()
, che è una funzione molto grande, ed è possibile che mi sia perso una proprietà o due dell'oggetto globale $wp
che avrei dovuto impostare. Quindi, se qualcosa non funziona come dovrebbe, fatemelo sapere e sarò felice di investigare e, se necessario, rivedere la risposta.
Comunque...

Dopo aver scritto questo, mi sono reso conto che ho testato per categorie invece che per termini tassonomici in generale, quindi il codice sopra non funzionerà per la tassonomia 'forum'
, comunque lo modificherò per farlo funzionare più tardi oggi...

Quindi ho aggiornato il codice per risolvere il problema che ho menzionato nel commento precedente.

non riesco a farlo funzionare... devo cambiare le regole di rewrite?

@One Trick Pony - Un po' più di informazioni diagnostiche sarebbero utili. :) Cosa hai provato? Cosa succede quando inserisci gli URL nel tuo browser? Per caso hai chiamato la tua tassonomia 'forums'
invece di 'forum'
? Ti aspetti che gli URL che puntano a queste pagine cambino (se sì, non c'è da stupirsi, il mio codice non si occupa della generazione degli URL, solo del routing degli URL.)

no, posso cambiare gli URL (penso sia la funzione term_link quella in cui devo agganciarmi per quello). site/rootforum/
funziona, ma site/rootforum/subforum/
no (errore 404) ...

@One Trick Pony - subforum
è un termine di tassonomia figlio di rootforum
? Per favore fornisci maggiori informazioni sul tuo setup attuale; è davvero difficile darti qualcosa che funzioni quando non sappiamo esattamente come è configurato il tuo sistema.

no, è la stessa tassonomia. subforum
è un termine figlio di rootforum

@One Trick Pony - Cosa succede quando "non funziona?" Quali URL (completi) non funzionano? Qual è il dominio effettivo dove questo non funziona? L'URL per /forums/foo/bar
funziona ma non /foo/bar
? Dammi più informazioni e farò in modo che funzioni per te, ma al momento sento di non avere abbastanza elementi per il debug dato che per me funziona perfettamente.

vedi - http://dev.digitalnature.ro/wp99420/forum1/ - poi prova ad accedere a http://dev.digitalnature.ro/wp99420/forum1/general-discussion/ (general-discussion
è un sotto-forum di forum1
)

se è rilevante, ecco l'intero codice per il sistema del forum: http://pastebin.com/N509zCh3

@One Trick Pony - Cos'è wp99420? È la root del sito? Il mio codice attualmente non gestisce l'individuazione della root di un sito in una sottodirectory; è quello che ti serve?

@One Trick Pony - Ho aggiornato il codice per gestire i siti in un sottodominio.

non funziona davvero, continuo a ricevere l'errore 404, ma grazie comunque. Ho appena scoperto il plugin bbpress quindi non lo userò più

@One Trick Pony - Vorrei davvero capire perché non funziona per te. Voglio trasformarlo in un plugin che chiunque possa usare per qualsiasi URL desideri, quindi scoprire perché non funziona nel tuo caso sarebbe utile.

beh, testandolo su localhost/wp
ottengo questo come $path_segments: Array ( [0] => wp [1] => rootforum [2] => subforum )
. $taxonomy_term e $taxonomy_slug sono anche a posto, quindi parse_request() sembra fare il suo lavoro. Forse il problema è parent::parse_request() che resetta l'array query_vars (vedi riga 123 in class-wp)?

@One Trick Pony - Qual è il tuo valore per WP_SITEURL
? $_SERVER['SERVER_NAME']
restituisce 'localhost'
? Io tendo a configurare domini "locali" per i test (es. http://mytest.dev
invece di http://localhost/wp
) quindi spesso non intercetto gli errori che si verificano in configurazioni non standard.

@One Trick Pony - Ho aggiornato il codice per non dipendere da WP_SITEURL; puoi provare di nuovo?

stesso... Comunque ho trovato il problema, almeno sul mio sistema - è la stringa implosa $path
che passi a query_vars['term']. Sembra rootforum/subform
(dovrebbe essere solo l'ultima parte, subforum
)

@One Trick Pony - rootforum
non è un termine genitore 'subforum'
della tassonomia 'forum'
?

rootforum è un termine, e subforum è un termine figlio di rootforum. entrambi fanno parte della tassonomia forum

@MikeSchinkel: spero davvero che quel ticket trac venga integrato nel core. Mi sembra un'ottima idea (con la mia comprensione limitata). BTW, hai questo plugin nel repository wp?

Ehi @MikeSchinkel, questa cosa ha mai avuto seguito? Le tue idee sono fantastiche e affrontano il mio principale problema con WP. Quel plugin è mai diventato realtà?

@PeterB - Grazie per i complimenti. Sto ancora lavorando a un plugin generico ma ho preparato un talk approfondito per WordCamp sull'URL Routing, le slide sono qui: http://hardcorewp.com/2014/hardcore-url-routing-for-wordpress/

Semplice, davvero.
Passo 1: Smetti completamente di usare il parametro rewrite. Implementeremo le nostre regole di riscrittura.
'rewrite'=>false;
Passo 2: Imposta regole verbose per le pagine. Questo forza le normali Pagine ad avere le proprie regole invece di essere un catch-all in fondo alla gerarchia.
Passo 3: Crea alcune regole di riscrittura per gestire i tuoi casi d'uso.
Passo 4: Forza manualmente il flush delle regole. Il modo più semplice: vai su Impostazioni->Permalink e clicca sul pulsante Salva. Preferisco questo metodo rispetto all'attivazione di un plugin per il mio utilizzo, poiché posso forzare il flush delle regole ogni volta che modifico qualcosa.
Ora, codice:
function test_init() {
// crea una nuova tassonomia
register_taxonomy(
'forum',
'post',
array(
'query_var' => true,
'public'=>true,
'label'=>'Forum',
'rewrite' => false,
)
);
// forza regole verbose.. questo fa sì che ogni Pagina abbia la propria regola invece di essere
// un catch-all, che useremo invece per la tassonomia forum
global $wp_rewrite;
$wp_rewrite->use_verbose_page_rules = true;
// due regole per gestire i feed
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]');
// una regola per gestire l'impaginazione dei post nella tassonomia
add_rewrite_rule('(.+)/page/?([0-9]{1,})/?$','index.php?forum=$matches[1]&paged=$matches[2]');
// una regola per mostrare normalmente la tassonomia forum
add_rewrite_rule('(.+)/?$', 'index.php?forum=$matches[1]');
}
add_action( 'init', 'test_init' );
Ricorda che dopo aver aggiunto questo codice, devi averlo attivo quando esegui il flush delle regole di permalink (salvando la pagina in Impostazioni->Permalink)!
Dopo aver eseguito il flush delle regole e salvato nel database, allora /qualsiasicosa dovrebbe puntare alla tua pagina della tassonomia forum=qualsiasicosa.
Le regole di riscrittura non sono così difficili se comprendi le espressioni regolari. Uso questo codice per aiutarmi nel debug:
function test_foot() {
global $wp_rewrite;
echo '<pre>';
var_dump($wp_rewrite->rules);
echo '</pre>';
}
add_action('wp_footer','test_foot');
In questo modo, posso vedere le regole correnti a colpo d'occhio nella mia pagina. Ricorda solo che per qualsiasi URL, il sistema parte dall'alto delle regole e le scorre fino a trovarne una che corrisponda. La corrispondenza viene poi usata per riscrivere la query in un set più normale di tipo ?chiave=valore. Queste chiavi vengono analizzate per ciò che va nell'oggetto WP_Query. Semplice.
Nota: Questo metodo probabilmente funzionerà solo se la tua struttura di post personalizzata inizia con qualcosa che non è un catchall, come %category% o qualcosa di simile. Devi iniziare con una stringa statica o numerica, come %year%. Questo per evitare che catturi il tuo URL prima che raggiunga le tue regole.

Se vuoi un debug più semplice delle tue regole di riscrittura, ti consiglio (ancora una volta) il mio plugin di analisi delle riscritture, che ti permette di testare le regole e vedere le variabili di query in tempo reale.

Sfortunatamente, l'attuale sistema di riscrittura degli URL forza l'appiattimento di tutti i potenziali pattern di URL in una lunga lista, invece di seguire la struttura ad albero intrinseca dei percorsi degli URL. L'attuale configurazione non può confrontarsi comodamente con un array di valori letterali come categorie o nomi di forum; come sai, forza tutti gli URL "Pagina" a essere valutati per primi. Il confronto per segmento del percorso e il matching in modi multipli (array di valori letterali, categorie, tag, termini di tassonomia, nomi utente, tipi di post, nomi di post, callback, hook di filtri, e infine RegEx) scalerebbe meglio per la complessità e sarebbe più facile da comprendere.

Mike: In realtà, non è affatto più facile da capire, perché non ho la minima idea di cosa diavolo stai parlando. Le tue idee sul routing degli URL sono confuse e difficili, e come probabilmente sai, non sono d'accordo con esse. La ricerca piatta ha più senso ed è più flessibile di quanto tu tenda a riconoscerle. La maggior parte delle persone non vuole tutta questa complessità inutile nei propri URL, e quasi nessuno ne ha bisogno.

Grazie, ma credo di aver già provato questa soluzione in precedenza (http://wordpress.stackexchange.com/questions/9455/custom-post-type-permalinks-giving-404s)

Fortunatamente WordPress Answers ora permette alle persone che vogliono davvero il controllo dei propri URL di avere finalmente una voce, e sembrano essere molte (100+). Ma rispetto il fatto che tu non possa seguire il mio esempio prima di un'implementazione completa. Prevedo che, una volta che l'approccio che sto sostenendo sarà completamente implementato in un plugin e dopo circa 6-12 mesi, diventerà il modo preferito per i siti CMS basati su WordPress per gestire i propri URL. Quindi riprendiamo questo dibattito tra circa 9 mesi.

Non sarai in grado di farlo utilizzando solo WP_Rewrite, poiché non può distinguere tra slug dei termini e slug dei post.
Devi anche agganciarti a 'request' e prevenire il 404, impostando la variabile di query del post invece di quella della tassonomia.
Qualcosa come questo:
function fix_post_request( $request ) {
$tax_qv = 'forum';
$cpt_name = 'post';
if ( !empty( $request[ $tax_qv ] ) ) {
$slug = basename( $request[ $tax_qv ] );
// se questo genererebbe un 404
if ( !get_term_by( 'slug', $slug, $tax_qv ) ) {
// imposta le variabili di query corrette
$request[ 'name' ] = $slug;
$request[ 'post_type' ] = $cpt_name;
unset( $request[$tax_qv] );
}
}
return $request;
}
add_filter( 'request', 'fix_post_request' );
Nota che la tassonomia deve essere definita prima del tipo di post.
Questo sarebbe un buon momento per sottolineare che avere una tassonomia e un tipo di post con la stessa variabile di query è una Cattiva Idea.
Inoltre, non sarai in grado di raggiungere i post che hanno lo stesso slug di uno dei termini.

Concordato che avere una tassonomia e un tipo di post con lo stesso query var sia una Cattiva Idea, ma questo potrebbe implicare per le persone che avere una tassonomia e un tipo di post con lo stesso nome sia una cattiva idea, il che non è il caso. Se si utilizza lo stesso nome, solo uno dei due dovrebbe avere un query var.

Dai un'occhiata al codice del plugin top level cats:
http://fortes.com/projects/wordpress/top-level-cats/
Potresti facilmente adattarlo per cercare lo slug della tua tassonomia personalizzata modificando
$category_base = get_option('category_base');
alla riga 74 con qualcosa come:
$category_base = 'forums';

Ti suggerirei di dare un'occhiata al plugin Custom Post Permalinks. Al momento non ho tempo per testarlo, ma potrebbe essere utile per la tua situazione.

Dato che sono familiare con la tua altra domanda, risponderò tenendola in considerazione.
Non ho testato affatto questo codice, ma potrebbe funzionare se lo esegui una volta subito dopo aver registrato tutti i permastruct che desideri:
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();
Ciò che fa questo codice: rimuove le regole di riscrittura generate dal permalink degli argomenti dal flusso normale dell'array delle regole e le riunisce alla fine dell'array. Questo impedisce a quelle regole di interferire con qualsiasi altra regola di riscrittura. Poi, forza le regole di riscrittura verbose (ogni pagina ottiene una regola individuale con un'espressione regolare specifica). Questo impedisce alle pagine di interferire con le regole dei tuoi argomenti. Infine, esegue un flush forzato (assicurati che il file .htaccess sia scrivibile, altrimenti non funzionerà) e salva l'array molto grande e complicato delle regole di riscrittura.

Rimuove lo slug del tipo aggiungendo una regola specifica per ogni pagina di custom post type.

Non sono sicuro che funzioni per le tassonomie, ma ha funzionato per i tipi di post personalizzati
Anche se non viene aggiornato da 2 anni, il seguente plugin ha funzionato per me: http://wordpress.org/plugins/remove-slug-from-custom-post-type/
Per tua informazione, sto utilizzando WP 3.9.1
con WP Types 1.5.7
