Rimuovere lo slug dagli URL dei post dei tipi di contenuto personalizzati
Sembra che tutte le risorse web basate sul tema della rimozione dello slug di un tipo di contenuto personalizzato, cioè
yourdomain.com/CPT-SLUG/post-name
siano ormai soluzioni molto datate che spesso fanno riferimento a installazioni precedenti a WP versione 3.5. Una soluzione comune è:
'rewrite' => array( 'slug' => false, 'with_front' => false ),
all'interno della funzione register_post_type
. Questo non funziona più ed è fuorviante. Quindi chiedo alla community nel Q4 2020...
Quali sono i modi moderni ed efficienti per rimuovere lo Slug del Tipo di Post dall'URL di un Post Type personalizzato dall'interno dell'argomento rewrite o da qualsiasi altra parte?
AGGIORNAMENTO: Sembrano esserci diversi modi per forzare questo funzionamento con le regex. In particolare la risposta di Jan Beck, nel caso in cui si sia costantemente disposti a monitorare la creazione dei contenuti per assicurarsi che non vengano creati nomi di pagine/post in conflitto.... Tuttavia sono convinto che questa sia una grande debolezza nel core di WP che dovrebbe essere gestita per noi. Sia come opzione/hook durante la creazione di un CPT sia come set avanzato di opzioni per i permalink. Si prega di supportare il ticket di track.
Nota a piè di pagina: Si prega di supportare questo ticket trac osservandolo/promuovendolo: https://core.trac.wordpress.org/ticket/34136#ticket

Il seguente codice funzionerà, ma devi tenere a mente che possono verificarsi facilmente conflitti se lo slug del tuo custom post type è uguale a quello di una pagina o articolo...
Per prima cosa, rimuoveremo lo slug dal permalink:
function na_remove_slug( $post_link, $post, $leavename ) {
if ( 'events' != $post->post_type || 'publish' != $post->post_status ) {
return $post_link;
}
$post_link = str_replace( '/' . $post->post_type . '/', '/', $post_link );
return $post_link;
}
add_filter( 'post_type_link', 'na_remove_slug', 10, 3 );
Rimuovere solo lo slug non è sufficiente. Al momento otterrai una pagina 404 perché WordPress si aspetta che solo articoli e pagine si comportino in questo modo. Dovrai anche aggiungere quanto segue:
function na_parse_request( $query ) {
if ( ! $query->is_main_query() || 2 != count( $query->query ) || ! isset( $query->query['page'] ) ) {
return;
}
if ( ! empty( $query->query['name'] ) ) {
$query->set( 'post_type', array( 'post', 'events', 'page' ) );
}
}
add_action( 'pre_get_posts', 'na_parse_request' );
Basta cambiare "events" con il tuo custom post type e sei a posto. Potresti dover aggiornare i tuoi permalink.

grazie. Pensi che questo sia meglio rispetto a creare i rewrite manualmente? Ho visto quella soluzione e potrebbe tenere a bada i conflitti che menzioni?

Penso che questa sia una buona soluzione automatizzata se sei sicuro di non creare conflitti. Non è una buona soluzione se la stai fornendo a... diciamo un cliente che non è esperto di tecnologia.

Fallisce con nginx a causa della condizione 2 != count( $query->query )
. Con nginx, puoi avere $query->query come array('page' => '', 'name' => '...', 'q' => '...')
. Quindi @NateAllen, qual è il significato di quella condizione?

Abbiamo bisogno di qualcosa di meglio di questo. Un supporto integrato per rimuovere lo slug in modo da non creare URL conflittuali in seguito. Il modo in cui i post regolari e le pagine creano i loro URL.

Sono solo io o questo rompe alcuni tag condizionali di WordPress come is_single() e is_singular()?

Questa soluzione purtroppo ha causato alcuni link interrotti e il mio blog ha smesso di mostrare i post, diventando semplicemente una pagina normale. Vedi una soluzione migliore qui sotto proposta da Matt Keys.

Questo codice presuppone che il nome del post_type
sia lo stesso dello slug
del custom post type, cosa che non è necessariamente vera in tutti i casi. Per il resto, ottima soluzione anche se concordo che una soluzione nativa di WP sarebbe migliore.

single-{cpt}.php smette di funzionare utilizzando questo approccio

Per chi ha problemi con il codice sopra, funziona perfettamente se sostituisci la seconda funzione ( function na_parse_request() ) con quella trovata in questa risposta. Non dimenticare di modificare il codice con il nome del tuo Custom Post Type.

Ho utilizzato questo bel codice fino all'arrivo di WP 5.2. Dopo l'aggiornamento, questo codice ha iniziato a non funzionare correttamente sul mio plugin di Custom Post Type e sul plugin Advanced Custom Fields, perché, credo, stanno usando la stessa funzione pre_get_posts, quindi invece dei gruppi di Advanced Custom Fields vedo i miei post personalizzati in questo plugin... Inoltre non funziona con il plugin CPT UI - non è più possibile creare nuovi post, non compaiono più nell'elenco dopo la creazione. Aiuto!!

Funziona per un singolo post type. Come posso utilizzare il codice per più post type?

Ahh, finalmente una soluzione - grazie! Mi chiedevo perché improvvisamente ricevessi errori 404 per un'ora prima di realizzare che era a causa di questa cosa 'rewrite' => array( 'slug' => false) :)

Esaminando le risposte qui presenti, credo ci sia spazio per una soluzione migliore che combina alcune cose che ho imparato sopra e aggiunge il rilevamento automatico e la prevenzione di slug di post duplicati.
NOTA: Assicurati di sostituire 'custom_post_type' con il nome del tuo CPT nell'esempio qui sotto. Ci sono molte occorrenze, e un 'trova/sostituisci' è un modo semplice per catturarle tutte. Tutto questo codice può essere inserito nel tuo functions.php o in un plugin.
Passo 1: Disabilita i rewrite sul tuo custom post type impostando rewrites a 'false' quando registri il post:
register_post_type( 'custom_post_type',
array(
'rewrite' => false
)
);
Passo 2: Aggiungi manualmente i nostri rewrite personalizzati in fondo ai rewrite di WordPress per il nostro custom_post_type
function custom_post_type_rewrites() {
add_rewrite_rule( '[^/]+/attachment/([^/]+)/?$', 'index.php?attachment=$matches[1]', 'bottom');
add_rewrite_rule( '[^/]+/attachment/([^/]+)/trackback/?$', 'index.php?attachment=$matches[1]&tb=1', 'bottom');
add_rewrite_rule( '[^/]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'bottom');
add_rewrite_rule( '[^/]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'bottom');
add_rewrite_rule( '[^/]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$', 'index.php?attachment=$matches[1]&cpage=$matches[2]', 'bottom');
add_rewrite_rule( '[^/]+/attachment/([^/]+)/embed/?$', 'index.php?attachment=$matches[1]&embed=true', 'bottom');
add_rewrite_rule( '([^/]+)/embed/?$', 'index.php?custom_post_type=$matches[1]&embed=true', 'bottom');
add_rewrite_rule( '([^/]+)/trackback/?$', 'index.php?custom_post_type=$matches[1]&tb=1', 'bottom');
add_rewrite_rule( '([^/]+)/page/?([0-9]{1,})/?$', 'index.php?custom_post_type=$matches[1]&paged=$matches[2]', 'bottom');
add_rewrite_rule( '([^/]+)/comment-page-([0-9]{1,})/?$', 'index.php?custom_post_type=$matches[1]&cpage=$matches[2]', 'bottom');
add_rewrite_rule( '([^/]+)(?:/([0-9]+))?/?$', 'index.php?custom_post_type=$matches[1]', 'bottom');
add_rewrite_rule( '[^/]+/([^/]+)/?$', 'index.php?attachment=$matches[1]', 'bottom');
add_rewrite_rule( '[^/]+/([^/]+)/trackback/?$', 'index.php?attachment=$matches[1]&tb=1', 'bottom');
add_rewrite_rule( '[^/]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'bottom');
add_rewrite_rule( '[^/]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'bottom');
add_rewrite_rule( '[^/]+/([^/]+)/comment-page-([0-9]{1,})/?$', 'index.php?attachment=$matches[1]&cpage=$matches[2]', 'bottom');
add_rewrite_rule( '[^/]+/([^/]+)/embed/?$', 'index.php?attachment=$matches[1]&embed=true', 'bottom');
}
add_action( 'init', 'custom_post_type_rewrites' );
NOTA: A seconda delle tue esigenze, potresti voler modificare i rewrite sopra indicati (disabilitare i trackback? i feed?, ecc.). Questi rappresentano i rewrite 'predefiniti' che sarebbero stati generati se non avessi disabilitato i rewrite nel passo 1
Passo 3: Rendi nuovamente 'belli' i permalink per il tuo custom post type
function custom_post_type_permalinks( $post_link, $post, $leavename ) {
if ( isset( $post->post_type ) && 'custom_post_type' == $post->post_type ) {
$post_link = home_url( $post->post_name );
}
return $post_link;
}
add_filter( 'post_type_link', 'custom_post_type_permalinks', 10, 3 );
NOTA: Puoi fermarti qui se non sei preoccupato che i tuoi utenti creino un post conflittuale (duplicato) in un altro tipo di post che creerà una situazione in cui solo uno di essi potrà essere caricato quando la pagina viene richiesta.
Passo 4: Previeni slug di post duplicati
function prevent_slug_duplicates( $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug ) {
$check_post_types = array(
'post',
'page',
'custom_post_type'
);
if ( ! in_array( $post_type, $check_post_types ) ) {
return $slug;
}
if ( 'custom_post_type' == $post_type ) {
// Salvataggio di un post di tipo custom_post_type, controlla duplicati nei tipi di post POST o PAGE
$post_match = get_page_by_path( $slug, 'OBJECT', 'post' );
$page_match = get_page_by_path( $slug, 'OBJECT', 'page' );
if ( $post_match || $page_match ) {
$slug .= '-duplicate';
}
} else {
// Salvataggio di un POST o PAGE, controlla duplicati nel tipo di post custom_post_type
$custom_post_type_match = get_page_by_path( $slug, 'OBJECT', 'custom_post_type' );
if ( $custom_post_type_match ) {
$slug .= '-duplicate';
}
}
return $slug;
}
add_filter( 'wp_unique_post_slug', 'prevent_slug_duplicates', 10, 6 );
NOTA: Questo aggiungerà la stringa '-duplicate' alla fine di qualsiasi slug duplicato. Questo codice non può prevenire slug duplicati se già esistono prima di implementare questa soluzione. Assicurati di controllare prima i duplicati.
Mi piacerebbe avere feedback da chiunque provi questa soluzione per vedere se ha funzionato bene anche per loro.

Speravo in questo approccio, ma mi restituisce un errore 404 sui miei post CPT, anche dopo aver ri-salvato i Permalink.

Mi dispiace che non abbia funzionato per te Garconis. Ne avevo parlato con qualcun altro tempo fa e anche loro avevano problemi sul loro sito. Mi sembra di ricordare che dipendesse dal fatto che i permalink dei post del blog avessero o meno un prefisso. Sul sito per cui ho sviluppato questa soluzione, i post del blog utilizzano la struttura dei permalink: /blog/%postname%/. Se i tuoi post del blog non hanno un prefisso, e se per te va bene aggiungerlo, provalo e fammi sapere come va!

Questa soluzione ha funzionato per me. A differenza di altre soluzioni sulla pagina, non ha rotto le pagine normali o il layout del blog, e non ha causato redirect infiniti. Mostra anche l'URL corretto nell'area "Permalink" quando si modificano quelle pagine CPT. Soluzione abbastanza buona qui, l'unica avvertenza è che la pagina archivio non funziona. RICORDA di sostituire "custom_post_type" e aggiornare i permalink successivamente.

@MattKeys, le impostazioni predefinite dei Permalink hanno una Struttura Personalizzata di /%category%/%postname%/
. Quando aggiungi il tuo codice, gli slug CPT appaiono OK (anche se mancano della barra finale) ... e anche il controllo dei conflitti funziona. Ma i risultati effettivi dei post mostrano un 404.

Funziona benissimo! Tuttavia, ho dovuto aggiungere una variabile di query nel passo 2 add_rewrite_tag( "%custom_post_type%", '([^/]+)', "post_type=custom_post_type&name=" );
per vedere le pagine CPT invece di un messaggio 404

Questa è l'unica soluzione che ha funzionato per me. Ho solo dovuto aggiungere una barra alla fine di return $post_link
nel passo 3.

Ha funzionato, ma manca la barra finale. Per favore aggiorna la risposta per includerla! Un potenziale svantaggio: non potrai più personalizzare il permalink dal front-end (ad esempio quando vuoi accorciarlo rispetto al titolo).

Funziona bene per il CPT, ma fa andare in 404 tutti gli articoli del mio sito. Sto usando questo codice in un plugin, quindi deve funzionare con ogni possibile struttura di permalink, e non sono sicuro che sia possibile.

Scrivi il seguente codice nella registrazione della tassonomia.
'rewrite' => [
'slug' => '/',
'with_front' => false
]
La cosa più importante che devi fare dopo aver modificato il codice
Dopo aver modificato il documento della tassonomia del tuo custom post type, prova ad andare in Impostazioni > Permalink e risalva le impostazioni, altrimenti otterrai un errore 404 pagina non trovata.

Funziona davvero, non so come nessuno l'abbia notato prima. Ovviamente questo può interferire con altre pagine se hanno lo stesso permalink, ma se non è così è un'ottima soluzione.

L'ho provato. Fornisce il risultato desiderato per i link del mio custom post type. Tuttavia "cattura" tutti gli slug POST o PAGE e cerca di risolverli come URL per il mio custom post type, generando poi errori 404. (sì, ho salvato i permalink).

Potrebbe esserci lo stesso slug per una pagina e un custom post type, cambia lo slug della tua pagina e poi verifica...

Non funziona. Restituisce errore 404 anche dopo aver aggiornato i permalink.

@ChristineCooper Devi seguire questo passaggio
Dopo aver modificato il documento della tassonomia del tuo custom post type, prova ad andare in Impostazioni > Permalink e ri-salva le impostazioni, altrimenti otterrai una pagina 404 non trovata.

Come evidenziato nel mio ultimo commento, otterrai un errore 404 anche dopo aver aggiornato i permalink. Per favore provalo tu stesso.

Funziona perfettamente, soprattutto quando si legge l'intero messaggio, inclusa la parte "salva nuovamente le tue impostazioni". +1

Anche dopo aver salvato nuovamente le impostazioni dei permalink, i post e le pagine non funzionano più (errore 404)

Questa soluzione funziona per rimuovere lo slug dall'URL. Ma le pagine archivio non funzionano più.

Come altri hanno affermato, questo funziona per i post CPT stessi. Ma ora sta causando un errore 404 per le Pagine regolari.

Forse questo funziona per alcuni ma non per altri a causa di altri plugin che interferiscono? Ad esempio, non funziona neanche per me in un ambiente dove WPML con sottodirectory è configurato: www.mysite.com/en/CPT-item/ dà errore 404.

Ho cercato di capirlo non molto tempo fa e la risposta breve, per quanto ne so, è no. Almeno non dall'interno dell'argomento rewrite.
La spiegazione lunga diventa evidente se guardi il codice effettivo di register_post_type
in wp-includes/post.php riga 1454:
add_permastruct( $post_type, "{$args->rewrite['slug']}/%$post_type%", $permastruct_args );
Puoi vedere che antepone $args->rewrite['slug']
al tag di rewrite %$post_type%
. Si potrebbe pensare "allora impostiamo semplicemente lo slug a null
" fino a quando non si guarda qualche riga più sopra:
if ( empty( $args->rewrite['slug'] ) )
$args->rewrite['slug'] = $post_type;
Puoi vedere che la funzione si aspetta sempre un valore di slug che non sia vuoto e altrimenti utilizza il post type.

Grazie @JanBeck. C'è una ragione importante per cui questa cosa esiste? Perché non modificare questo file core con un condizionale per escludere certi tipi di post da questa regola?

Dovresti assegnare la risposta a Jan Beck. WordPress ha bisogno dello slug del post_type per instradare correttamente le richieste. Questa regola previene conflitti di nomi tra le pagine native di WP (che vengono visualizzate senza lo slug) e qualsiasi tipo di post personalizzato definito. Se rimuovi lo slug, WordPress non saprà la differenza tra una pagina chiamata "picnic" e un evento (custom post type) chiamato "picnic".

Rassegna Plugin
Siamo quasi nel 2020 e molte di queste risposte non funzionano più. Ecco la mia panoramica sulle opzioni attuali:
- La risposta di Matt Keys sembra essere l'unica sulla strada giusta se cerchi una soluzione personalizzata. Nessuno dei plugin che ho trovato fa tutto ciò che è elencato qui, soprattutto il controllo dei duplicati. Questo approccio sembra un'ottima opportunità per un plugin se qualcuno volesse cimentarsi.
- Permalink Manager Lite
- Il migliore tra i plugin gratuiti che ho provato.
- Offre il controllo completo sulla struttura dei permalink di Pagine/Articoli/CPT e permette che siano uguali. L'interfaccia grafica è di gran lunga la più ricca di funzionalità.
- Permette di sovrascrivere completamente ogni singolo articolo e mostra qual era il permalink originale/predefinito, con la possibilità di resettarlo se necessario.
- Supporta il multi-sito.
- Non controlla i duplicati tra tipi di contenuto, il che è un peccato. Se una pagina e un CPT hanno lo stesso URL, viene caricata la pagina e il CPT risulta inaccessibile. Nessun avviso o errore, devi controllare manualmente i duplicati.
- Tutte le funzionalità relative alle tassonomie sono nella versione PRO. I reminder per l'aggiornamento sono piuttosto invadenti.
- Custom Permalinks
- La versione gratuita offre molte funzionalità. I permalink per le tassonomie e il supporto premium sembrano essere le uniche cose riservate alla versione pro.
- Permette di modificare il permalink completo per ogni singola pagina/articolo/CPT.
- Supporta il multi-sito.
- Non permette di modificare la struttura predefinita, quindi i tuoi Custom Post Type avranno ancora la forma esempio.com/slug-cpt/titolo-articolo ma puoi modificarli individualmente.
- Non controlla i duplicati tra tipi di contenuto, il che è un peccato.
- Custom Post Type Permalinks
- Permette agli utenti non sviluppatori di modificare le cose che sono già facili da cambiare con
register_post_type
- Non permette di modificare lo slug base del CPT - solo la parte che viene dopo - quindi praticamente inutile per gli sviluppatori e per il problema sollevato in questa domanda.
- Permette agli utenti non sviluppatori di modificare le cose che sono già facili da cambiare con
- remove base slug... - abbandonato da diversi anni... da non usare.

Il plugin Permalink Manager Lite è senza dubbio la soluzione migliore: stabile, robusta, pulita e la versione gratuita ti consente di rimuovere la base dello slug. Funziona anche con Polylang! Testato su WordPress 5.4, con il tema TwentyTwenty, senza altri plugin attivati. Funziona perfettamente con i Custom Post Type, indipendentemente dal fatto che tu abbia creato un tipo gerarchico (con post figlio e nipote). Per chiunque cerchi una soluzione pulita.

Bella panoramica. Il codice della mia risposta è stato eseguito su un sito client per anni senza alcun problema. L'unico svantaggio che alcuni hanno sottolineato nella mia soluzione è che non funziona sui permalink 'predefiniti' per il tipo di post 'blog' incorporato (post). Invece deve esserci un prefisso come: /blog/%postname%/. Per chi usa WordPress come CMS, potrebbero già farlo, ma per altri potrebbe essere un blocco purtroppo.

In risposta alla mia precedente risposta:
potresti ovviamente impostare il parametro rewrite
su false
quando registri un nuovo post type e gestire manualmente le regole di riscrittura in questo modo
<?php
function wpsx203951_custom_init() {
$post_type = 'event';
$args = (object) array(
'public' => true,
'label' => 'Eventi',
'rewrite' => false, // impostare sempre su false
'has_archive' => true
);
register_post_type( $post_type, $args );
// questi sono i tuoi argomenti effettivi per la riscrittura
$args->rewrite = array(
'slug' => 'calendario'
);
// tutto ciò che segue è preso dalla funzione register_post_type
if ( is_admin() || '' != get_option( 'permalink_structure' ) ) {
if ( ! is_array( $args->rewrite ) )
$args->rewrite = array();
if ( empty( $args->rewrite['slug'] ) )
$args->rewrite['slug'] = $post_type;
if ( ! isset( $args->rewrite['with_front'] ) )
$args->rewrite['with_front'] = true;
if ( ! isset( $args->rewrite['pages'] ) )
$args->rewrite['pages'] = true;
if ( ! isset( $args->rewrite['feeds'] ) || ! $args->has_archive )
$args->rewrite['feeds'] = (bool) $args->has_archive;
if ( ! isset( $args->rewrite['ep_mask'] ) ) {
if ( isset( $args->permalink_epmask ) )
$args->rewrite['ep_mask'] = $args->permalink_epmask;
else
$args->rewrite['ep_mask'] = EP_PERMALINK;
}
if ( $args->hierarchical )
add_rewrite_tag( "%$post_type%", '(.+?)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&pagename=" );
else
add_rewrite_tag( "%$post_type%", '([^/]+)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&name=" );
if ( $args->has_archive ) {
$archive_slug = $args->has_archive === true ? $args->rewrite['slug'] : $args->has_archive;
if ( $args->rewrite['with_front'] )
$archive_slug = substr( $wp_rewrite->front, 1 ) . $archive_slug;
else
$archive_slug = $wp_rewrite->root . $archive_slug;
add_rewrite_rule( "{$archive_slug}/?$", "index.php?post_type=$post_type", 'top' );
if ( $args->rewrite['feeds'] && $wp_rewrite->feeds ) {
$feeds = '(' . trim( implode( '|', $wp_rewrite->feeds ) ) . ')';
add_rewrite_rule( "{$archive_slug}/feed/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' );
add_rewrite_rule( "{$archive_slug}/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' );
}
if ( $args->rewrite['pages'] )
add_rewrite_rule( "{$archive_slug}/{$wp_rewrite->pagination_base}/([0-9]{1,})/?$", "index.php?post_type=$post_type" . '&paged=$matches[1]', 'top' );
}
$permastruct_args = $args->rewrite;
$permastruct_args['feed'] = $permastruct_args['feeds'];
add_permastruct( $post_type, "%$post_type%", $permastruct_args );
}
}
add_action( 'init', 'wpsx203951_custom_init' );
Puoi vedere che la chiamata add_permastruct
ora non include più lo slug.
Ho testato due scenari:
- Quando ho creato una pagina con lo slug "calendario", quella pagina viene sovrascritta dall'archivio del post type che utilizza anch'esso lo slug "calendario".
- Quando ho creato una pagina con lo slug "mio-evento" e un evento (CPT) con lo slug "mio-evento", viene visualizzato il custom post type.
- Qualsiasi altra pagina non funziona. Se guardi l'immagine sopra diventa chiaro il perché: la regola del custom post type corrisponderà sempre a uno slug di pagina. Poiché WordPress non ha modo di identificare se si tratta di una pagina o di un custom post type che non esiste, restituirà un errore 404. Ecco perché hai bisogno di uno slug per identificare se si tratta di una pagina o di un CPT. Una possibile soluzione sarebbe intercettare l'errore e cercare una pagina che potrebbe esistere similmente a questa risposta.

Quindi se l'obiettivo è rimuovere lo slug per i CPT, non potremmo semplicemente dare ai CPT un nome univoco che non entri in conflitto, visto che comunque non apparirà nell'URL? O è il nome del post che potrebbe creare conflitti se uguale a una pagina?

Ho aggiornato la mia risposta per mostrare che questo in realtà rompe tutte le pagine. Senza uno slug, WP cercherà un CPT invece di una pagina e, se non lo trova, restituirà un errore. Quindi in realtà non è legato al nome del post.

Capisco. Dovrebbero esserci regole di riscrittura che aggiungono '-1' agli URL in conflitto futuri, come avviene nativamente tra i post e le pagine di WP. Ho creato un ticket trac https://core.trac.wordpress.org/ticket/34136#ticket e mi piacerebbe conoscere il tuo parere.

Contesto
Anche dopo aver cercato ovunque, non sono riuscito a trovare una soluzione adeguata per rimuovere lo slug CPT dai permalink che funzioni davvero e sia coerente con il modo in cui WordPress analizza effettivamente le richieste. Sembra che tutti gli altri alla ricerca della stessa soluzione siano nella mia stessa situazione.
Si scopre che si tratta in realtà di una soluzione in due parti.
- Rimuovere lo slug CPT dai permalink
- Indicare a WordPress come trovare i post dai nuovi permalink
La prima parte è abbastanza semplice e molte risposte esistenti l'hanno già corretta. Ecco come si presenta:
// rimuovi lo slug CPT dai permalink
function remove_cpt_slug( $post_link, $post, $leavename ) {
if ( $post->post_type != 'custom_post_type' ) {
return $post_link;
} else {
$post_link = str_replace( '/' . $post->post_type . '/', '/', $post_link );
return $post_link;
}
}
add_filter( 'post_type_link', 'remove_cpt_slug', 10, 3 );
Ora, la seconda parte è dove le cose si complicano. Dopo aver risolto la prima parte, i tuoi permalink CPT non hanno più gli slug CPT. Ma ora il problema è che WordPress non sa come trovare i tuoi post da quei nuovi permalink perché tutto ciò che sa è che i permalink CPT hanno gli slug CPT. Quindi, senza uno slug CPT nel permalink, non riesce a trovare il tuo post. Ecco perché quando fai una richiesta per i tuoi post a questo punto, restituisce un errore 404 non trovato.
Quindi, tutto ciò che devi fare ora è indicare a WordPress come trovare i tuoi post utilizzando i nuovi permalink. Ma questa è la parte in cui le risposte esistenti non funzionano molto bene. Diamo un'occhiata ad alcune di queste risposte per esempio:
La funzione qui sotto funziona abbastanza bene ma funzionerà solo se la struttura dei permalink è impostata su Nome articolo.
function parse_request_remove_cpt_slug( $query ) {
if ( ! $query->is_main_query() || 2 != count( $query->query ) || ! isset( $query->query['page'] ) ) {
return;
}
if ( ! empty( $query->query['name'] ) ) {
global $wpdb;
$cpt = $wpdb->get_var("SELECT post_type FROM $wpdb->posts WHERE post_name = '{$query->query['name']}'");
// Aggiungi CPT alla lista dei tipi di post che WP includerà quando esegue una query basata sul nome del post.
$query->set( 'post_type', $cpt );
}
}
add_action( 'pre_get_posts', 'parse_request_remove_cpt_slug' );
La funzione qui sotto funziona bene per il tuo tipo di post personalizzato indipendentemente dalla struttura del permalink, tuttavia genererà un errore su tutti gli altri tipi di post.
function rewrite_rule_remove_cpt_slug() {
add_rewrite_rule(
'(.?.+?)(?:/([0-9]+))?/?$',
'index.php?custom_post_type=$matches[1]/$matches[2]&post_type=custom_post_type',
'bottom'
);
}
add_action( 'init', 'rewrite_rule_remove_cpt_slug', 1, 1 );
C'è un'altra risposta che dovrebbe funzionare come soluzione autonoma ma finisce per causare più problemi che soluzioni, come generare errori nei tuoi post CPT così come in altri. Questa richiede la modifica dell'argomento rewrite nella registrazione del tuo CPT come segue:
'rewrite' => array( 'slug' => '/', 'with_front' => false )
Finora, tutte le risposte esistenti che ho trovato sono come quelle sopra. O funzionano parzialmente o non funzionano più. Questo probabilmente perché WordPress non fornisce un modo semplificato per rimuovere lo slug CPT dai permalink dei tipi di post personalizzati e quindi queste risposte sono basate su scenari particolari o su un modo approssimativo.
Risposta
Ecco cosa ho ideato cercando di creare una soluzione che funziona nella maggior parte se non in tutti gli scenari. Questo rimuoverà correttamente lo slug CPT dai permalink CPT e indicherà a WordPress come trovare i post CPT da quei nuovi permalink. Non riscrive le regole nel database quindi non sarà necessario salvare nuovamente la struttura dei permalink. Inoltre, questa soluzione è coerente con il modo in cui WordPress analizza effettivamente le richieste per trovare i post dai permalink, il che aiuta a renderla una soluzione più accettabile.
Assicurati di sostituire custom_post_type
con il nome del tuo tipo di post personalizzato. Appare una volta in ogni funzione quindi due occorrenze in totale.
// rimuovi lo slug CPT dai permalink
function remove_cpt_slug( $post_link, $post, $leavename ) {
if ( $post->post_type != 'custom_post_type' ) {
return $post_link;
} else {
$post_link = str_replace( '/' . $post->post_type . '/', '/', $post_link );
return $post_link;
}
}
add_filter( 'post_type_link', 'remove_cpt_slug', 10, 3 );
// indica a wordpress come trovare i post dai nuovi permalink
function parse_request_remove_cpt_slug( $query_vars ) {
// esci se siamo nel pannello di amministrazione
if ( is_admin() ) {
return $query_vars;
}
// esci se i permalink personalizzati non sono abilitati
if ( ! get_option( 'permalink_structure' ) ) {
return $query_vars;
}
$cpt = 'custom_post_type';
// memorizza il valore dello slug del post in una variabile
if ( isset( $query_vars['pagename'] ) ) {
$slug = $query_vars['pagename'];
} elseif ( isset( $query_vars['name'] ) ) {
$slug = $query_vars['name'];
} else {
global $wp;
$path = $wp->request;
// usa il percorso dell'url come slug
if ( $path && strpos( $path, '/' ) === false ) {
$slug = $path;
} else {
$slug = false;
}
}
if ( $slug ) {
$post_match = get_page_by_path( $slug, 'OBJECT', $cpt );
if ( ! is_admin() && $post_match ) {
// rimuovi qualsiasi elemento di errore 404 non trovato dall'array query_vars perché esiste già una corrispondenza nel cpt
if ( isset( $query_vars['error'] ) && $query_vars['error'] == 404 ) {
unset( $query_vars['error'] );
}
// rimuovi elementi non necessari dall'array query_vars originale
unset( $query_vars['pagename'] );
// aggiungi elementi necessari nell'array query_vars
$query_vars['post_type'] = $cpt;
$query_vars['name'] = $slug;
$query_vars[$cpt] = $slug; // questo costruisce l'elemento "cpt=>post_slug"
}
}
return $query_vars;
}
add_filter( 'request', "parse_request_remove_cpt_slug" , 1, 1 );
Considerazioni:
Questa soluzione lascia intenzionalmente fuori dal suo ambito la struttura di permalink Semplice poiché non è una delle strutture di permalink personalizzati. Quindi, funzionerà con tutte le strutture di permalink tranne quella Semplice.
Poiché WordPress non previene automaticamente la creazione di slug duplicati tra diversi tipi di post, potresti riscontrare problemi nell'accesso ai post che hanno lo stesso slug a causa della perdita di unicità nei permalink CPT dopo la rimozione degli slug CPT. Questo codice non include alcuna funzionalità per prevenire tale comportamento, quindi potresti voler trovare una soluzione separata per affrontare questo problema.
In caso di un permalink duplicato, questo codice darà priorità al tuo CPT rispetto agli altri e quindi visualizzerà il post nel tuo CPT quando richiesto.

Al momento della scrittura, questa soluzione è la migliore. È semplice e corretta. La sua logica è facile da comprendere e personalizzare. Infatti, ho personalizzato questa soluzione per rimuovere anche gli slug delle tassonomie.

Per evitare la creazione di slug duplicati, puoi leggere e personalizzare il codice nel "Passo 4" della risposta di Matt Keys.

Ho consolidato questo codice e quello di Matt Keys in questo gist, necessita ulteriori test ma sembra funzionare bene. Fondamentalmente ho solo modificato il -duplicato per lo slug in qualcosa come -1,-2 ecc. Sicuramente c'è un modo migliore per farlo. Potrebbe anche essere carino costruire una piccola interfaccia attorno per impostare 'location' allo slug CPT desiderato. https://gist.github.com/cdsaenz/291d9599e1f20313c3a87edf48233176

e possiamo apportare alcune modifiche alla funzione menzionata sopra:
function na_parse_request( $query ) {
if ( ! $query->is_main_query() || 2 != count( $query->query ) || ! isset( $query->query['page'] ) ) {
return;
}
if ( ! empty( $query->query['name'] ) ) {
$query->set( 'post_type', array( 'post', 'events', 'page' ) );
}
}
in:
function na_parse_request( $query ) {
if ( ! $query->is_main_query() || 2 != count( $query->query ) || ! isset( $query->query['page'] ) ) {
return;
}
if ( ! empty( $query->query['name'] ) ) {
global $wpdb;
$pt = $wpdb->get_var(
"SELECT post_type FROM `{$wpdb->posts}` " .
"WHERE post_name = '{$query->query['name']}'"
);
$query->set( 'post_type', $pt );
}
}
per impostare il valore corretto di post_type.

Per chiunque stia leggendo questo post e abbia avuto problemi con i post figli come ho avuto io, ho scoperto che il modo migliore è aggiungere le proprie regole di rewrite.
Il problema principale che ho riscontrato è che WordPress gestisce il reindirizzamento da pagine che sono a 2 livelli di profondità (post figli) in modo leggermente diverso rispetto a pagine a 3 livelli di profondità (figli di post figli).
Ciò significa che quando ho /tipo-di-post/nome-post/figlio-post/ posso usare /nome-post/figlio-post e verrò reindirizzato alla versione con tipo-di-post all'inizio, ma se ho tipo-di-post/nome-post/figlio-post/nipote-post allora non posso usare nome-post/figlio-post/nipote-post.
Analizzando le regole di rewrite, sembra che corrisponda a elementi diversi da pagename al primo e secondo livello (penso che il secondo livello corrisponda agli allegati) e poi esegue qualcosa per reindirizzarti al post corretto. A tre livelli di profondità non funziona.
La prima cosa da fare è rimuovere il link del post type anche dai figli. Questa logica dovrebbe avvenire qui se guardi la risposta di Nate Allen sopra:
$post_link = str_replace( '/' . $post->post_type . '/', '/', $post_link );
Personalmente ho usato un mix di diversi condizionali per verificare se il post aveva figli e simili per ottenere il permalink corretto. Questa parte non è troppo complicata e troverai esempi di persone che lo fanno altrove.
Il passo successivo però è dove le cose cambiano rispetto alla risposta data. Invece di aggiungere elementi alla query principale (che funzionava per i post personalizzati e i loro figli ma non per i figli successivi) ho aggiunto una regola di rewrite che andava in fondo alle regole di WordPress in modo che se pagename non fosse verificato e fosse sul punto di restituire un 404, avrebbe effettuato un ultimo controllo per vedere se una pagina all'interno del custom post type aveva lo stesso nome, altrimenti avrebbe restituito il 404.
Ecco la regola di rewrite che ho usato assumendo che 'event' sia il nome del tuo CPT
function rewrite_rules_for_removing_post_type_slug()
{
add_rewrite_rule(
'(.?.+?)(?:/([0-9]+))?/?$',
'index.php?event=$matches[1]/$matches[2]&post_type=event',
'bottom'
);
}
add_action('init', 'rewrite_rules_for_removing_post_type_slug', 1, 1);
Spero che questo aiuti qualcun altro, non ho trovato nient'altro che riguardasse i figli dei post figli e la rimozione dello slug da quelli.

Sembra esserci un errore di battitura nella regex. Tra '(:' è necessario un '?' per usarlo come sotto-pattern non catturante => '(?:'. Il terzo ? sembra posizionato male in quanto permette un primo sotto-pattern vuoto. Probabilmente dovrebbe essere posizionato tra ( e :. Senza questo errore di battitura l'espressione sarà la stessa che si può trovare per il post type integrato 'page'.

Ho avuto gli stessi problemi qui e lì e sembra non esserci alcun movimento sul sito WordPress. Nella mia situazione particolare, in cui per i singoli articoli del blog era necessaria la struttura /blog/%postname%/, questa soluzione
https://kellenmace.com/remove-custom-post-type-slug-from-permalinks/
ha prodotto una serie di errori 404
Ma insieme a questo meraviglioso approccio, che non utilizza la struttura dei permalink del backend per l'articolo del blog, alla fine funziona perfettamente. https://www.bobz.co/add-blog-prefix-permalink-structure-blog-posts/
Grazie mille.

Ho provato e ha funzionato.
Questo è il codice semplice che devi utilizzare
register_post_type('wporg_product', array( 'labels' => array( 'name' => 'Portfolio', 'singular_name' => 'Portfolio', ), 'menu_icon' => 'dashicons-id', 'rewrite' => array( 'slug' => 'portfolio', 'with_front' => false ), // il mio slug personalizzato
)
);
Dopo aver modificato questo codice, devi salvare le impostazioni dei permalink una volta.

Questo è ciò che ha funzionato per me. Sostituisci podcast
con lo slug del tuo CPT (Custom Post Type):
add_action('init', function () {
register_post_type(
'podcast',
[
'rewrite' => false,
]
);
});
add_filter('post_type_link', function ($post_link, $post, $leavename) {
if (isset($post->post_type) && $post->post_type === 'podcast') {
$post_link = home_url($post->post_name);
}
return $post_link;
}, 10, 3);
add_action('init', function () {
add_rewrite_rule('(.+?)/?$', 'index.php?podcast=$matches[1]', 'bottom');
});

Il plugin "Remove CPT base" funziona.

Non è necessario scrivere così tanto codice hard-code. Basta utilizzare un plugin leggero:
Ha opzioni personalizzabili.

Ora capisco perché sei stato downvotato, impedisce la risoluzione dei normali collegamenti alle pagine. Non l'avevo notato perché ottenevo copie memorizzate nella cache delle pagine esistenti nonostante il refresh.

I collegamenti alle pagine (che non erano del custom post type) dal menu principale restituivano errori 404, come se la pagina non esistesse; tutto qui.

@Walf puoi darmi un esempio di URL del tuo caso? (puoi coprire il nome del dominio se vuoi, mi serve solo un esempio) grazie, lo aggiornerò
