Struttura URL per Custom Post Type e Tassonomie

13 feb 2018, 21:33:31
Visualizzazioni: 23.6K
Voti: 8

Ho un custom post type products e una tassonomia personalizzata 'Categorie Prodotti' con lo slug products.

Il mio obiettivo: Sto cercando di creare una struttura URL come questa:

Archivio Post Type Prodotti: products/

Archivi Categoria Prodotti di primo livello: products/product-category

Archivi Categoria Prodotti: products/product-category/product-sub-category

Pagina Prodotto: products/product-category/product-sub-category/product-page

Il problema: Le pagine dei prodotti sembrano utilizzare solo le categorie effettive. Quindi la struttura URL appare come products/uncategorized/product-page. Le pagine di archivio delle categorie non mantengono il base del cpt. Quindi risultano come category/sub-category.

Cosa ho provato: Innumerevoli ricerche su Google, diversi snippet (quelli che ricordo li includerò qui sotto), alcune funzioni personalizzate e alcuni plugin. Nessun risultato.

Ho il seguente codice.

add_action( 'init', 'products_cpt', 0);
add_action( 'init', 'register_taxonomies', 0 );

//Un custom post type per tutti i prodotti
function products_cpt(){
    $labels = array(
        'name'               => _x('Prodotti', 'Nome Generale Tipo Post'),
        'singular_name'      => _x('Prodotto', 'Nome Singolare Tipo Post'),
        'menu_name'          => __('Prodotti'),
        'parent_item_colon'  => __('Prodotto Genitore'),
        'all_items'          => __('Tutti i Prodotti'),
        'view_item'          => __('Visualizza Prodotto'),
        'add_new_item'       => __('Aggiungi Nuovo Prodotto'),
        'add_new'            => __('Aggiungi Nuovo'),
        'edit_item'          => __('Modifica Prodotto'),
        'update_item'        => __('Aggiorna Prodotto'),
        'search_items'       => __('Cerca Prodotti'),
        'not_found'          => __('Non Trovato'),
        'not_found_in_trash' => __('Non Trovato nel Cestino')
    );
    $supports = array( 
        'title', 
        'editor', 
        'excerpt', 
        'thumbnail', 
        'custom-fields', 
        'revisions' 
    );
    $args = array(
        'labels'              => $labels,
        'hierarchical'        => true,
        'public'              => true,
        'exclude_from_search' => false,
        'show_in_admin_bar'   => true,
        'show_in_nav_menus'   => true,
        'publicly_queryable'  => true,
        'query_var'           => true,
        'taxonomies'          => array( 'chemicals' ),
        'supports'            => $supports,
        'has_archive'         => 'products'
    );
    register_post_type('products', $args);
}

function register_taxonomies() {
    $taxonomies = array(
        'products' => array(
            'Prodotto',
            'Prodotti'
        ),
    );
    foreach($taxonomies as $slug => $name){
        create_product_taxonomy($slug,$name[0],$name[1]);
    }
}

function create_product_taxonomy($slug, $singular, $plural) {
    $labels = array(
        'name'                       => _x( 'Categorie '.$singular, 'Nome Generale Tassonomia', 'text_domain' ),
        'singular_name'              => _x( 'Categoria '.$singular, 'Nome Singolare Tassonomia', 'text_domain' ),
        'menu_name'                  => __( 'Categorie '.$singular, 'text_domain' ),
        'all_items'                  => __( 'Tutte le Categorie '.$singular, 'text_domain' ),
        'parent_item'                => __( 'Categoria '.$singular.' Genitore', 'text_domain' ),
        'parent_item_colon'          => __( 'Categoria '.$singular.' Genitore:', 'text_domain' ),
        'new_item_name'              => __( 'Nuovo Nome Categoria '.$singular, 'text_domain' ),
        'add_new_item'               => __( 'Aggiungi Nuova Categoria '.$singular, 'text_domain' ),
        'edit_item'                  => __( 'Modifica Categoria '.$singular, 'text_domain' ),
        'update_item'                => __( 'Aggiorna Categoria '.$singular, 'text_domain' ),
        'view_item'                  => __( 'Visualizza Categoria '.$singular, 'text_domain' ),
        'separate_items_with_commas' => __( 'Separa le Categorie '.$singular.' con virgole', 'text_domain' ),
        'add_or_remove_items'        => __( 'Aggiungi o rimuovi Categorie '.$singular, 'text_domain' ),
        'choose_from_most_used'      => __( 'Scegli tra le Categorie '.$singular.' più usate', 'text_domain' ),
        'popular_items'              => __( 'Categorie '.$singular.' Popolari', 'text_domain' ),
        'search_items'               => __( 'Cerca Categorie '.$singular, 'text_domain' ),
        'not_found'                  => __( 'Non Trovato', 'text_domain' ),
        'no_terms'                   => __( 'Nessuna Categoria '.$singular, 'text_domain' ),
        'items_list'                 => __( 'Lista Categorie '.$singular, 'text_domain' ),
        'items_list_navigation'      => __( 'Navigazione lista Categorie '.$singular, 'text_domain' ),
    );
     $args = array(
        'labels'                     => $labels,
        'hierarchical'               => true,
        'public'                     => true,
        'show_ui'                    => true,
        'show_admin_column'          => true,
        'show_in_nav_menus'          => true,
        'show_tagcloud'              => true,
        'has_archive'                => $plural,
        'rewrite' => array( 
            'slug'         => 'products',
            'with_front'   => true,
            'hierarchical' => true
        )
    );
    register_taxonomy( $slug, 'products', $args );
}

Prima di tutto, ho impostato le impostazioni dei permalink come segue: Impostazioni Permalink

Successivamente, ho provato ad aggiungere il seguente codice, tuttavia ha funzionato solo per le pagine dei prodotti. Gli Archivi delle Categorie Prodotti continuavano a dare errore 404.

add_filter( 'post_type_link', 'products_post_link', 1, 3 );
function products_post_link( $post_link, $id = 0 ){
    $post = get_post($id);  
    if ( is_object( $post ) ){
        $terms = wp_get_object_terms( $post->ID, 'products_category' );
        $slug_url = '';
        $last_id = 0;
        if( $terms ){
            foreach($terms as $term) {
                if ($term === reset($terms)){
                    foreach($terms as $termInner){
                        if($termInner->term_id == 0){
                            $slug_url .= $termInner->slug.'/';
                        }
                    }
                }elseif ($term === end($terms)){
                    foreach($terms as $termInner){
                        if($termInner->parent == $last_id){
                            $slug_url .= $termInner->slug;
                            $last_id = $termInner->term_id;
                        }
                    }
                }else{
                    foreach($terms as $termInner){
                        if($termInner->parent == $last_id){
                            $slug_url .= $termInner->slug.'/';
                            $last_id = $termInner->term_id;
                        }
                    }
                }
            }
            return str_replace( '%category%' , $slug_url , $post_link );
        }
    }
    return $post_link;  
}

E questo alla funzione di init del CPT:

    'rewrite' => array(
        'slug'         => 'products/%category%',
        'with_front'   => false,
        'hierarchical' => true,
    ),

Ho anche provato il seguente codice, non ricordo cosa è successo ma ricordo che non ha funzionato:

add_action('init', 'custom_resource_rewrite_rules');
function custom_resource_rewrite_rules() {
    add_rewrite_rule('products/([A-Za-z0-9\-\_]+)/?', '$matches[1]', 'top');
}

Ho anche provato a giocare con il filtro term_link. Nessun risultato.

Infine, ho provato i seguenti Plugin, anche questi senza successo.

Custom Permalinks

Permalinks Customizer

Custom Post Type Permalinks

Qualcuno ha una soluzione per questo? Mi sto strappando i capelli.

2
Commenti

Potresti mai avere products/product-category/product-page?

MikeSchinkel MikeSchinkel
15 feb 2018 04:51:21

@MikeSchinkel Suppongo, se l'unica categoria selezionata fosse una categoria senza categorie genitore

J Robz J Robz
18 feb 2018 00:21:35
Tutte le risposte alla domanda 2
2

Finalmente, ho trovato una soluzione!

Primo passo: registriamo il custom post type e la tassonomia personalizzata:

add_action( 'init', 'register_sps_products_post_type' );
function register_sps_products_post_type() {
    register_post_type( 'sps-product',
        array(
            'labels' => array(
                'name' => 'Prodotti',
                'menu_name' => 'Gestione Prodotti',
                'singular_name' => 'Prodotto',
                'all_items' => 'Tutti i Prodotti'
            ),
            'public' => true,
            'publicly_queryable' => true,
            'show_ui' => true,
            'show_in_menu' => true,
            'show_in_nav_menus' => true,
            'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'comments', 'post-formats', 'revisions' ),
            'hierarchical' => false,
            'has_archive' => 'products',
            'taxonomies' => array('product-category'),
            'rewrite' => array( 'slug' => 'products/%product_category%', 'hierarchical' => true, 'with_front' => false )
        )
    );
    register_taxonomy( 'product-category', array( 'sps-product' ),
        array(
            'labels' => array(
                'name' => 'Categorie Prodotti',
                'menu_name' => 'Categorie Prodotti',
                'singular_name' => 'Categoria Prodotto',
                'all_items' => 'Tutte le Categorie'
            ),
            'public' => true,
            'hierarchical' => true,
            'show_ui' => true,
            'rewrite' => array( 'slug' => 'products', 'hierarchical' => true, 'with_front' => false ),
        )
    );
}

Successivamente, aggiungiamo una nuova regola di riscrittura in modo che WordPress sappia interpretare la nostra nuova struttura di permalink:

add_action( 'generate_rewrite_rules', 'register_product_rewrite_rules' );
function register_product_rewrite_rules( $wp_rewrite ) {
    $new_rules = array( 
        'products/([^/]+)/?$' => 'index.php?product-category=' . $wp_rewrite->preg_index( 1 ), // 'products/qualsiasi-carattere/'
        'products/([^/]+)/([^/]+)/?$' => 'index.php?post_type=sps-product&product-category=' . $wp_rewrite->preg_index( 1 ) . '&sps-product=' . $wp_rewrite->preg_index( 2 ), // 'products/qualsiasi-carattere/slug-articolo/'
        'products/([^/]+)/([^/]+)/page/(\d{1,})/?$' => 'index.php?post_type=sps-product&product-category=' . $wp_rewrite->preg_index( 1 ) . '&paged=' . $wp_rewrite->preg_index( 3 ), // corrisponde ai risultati impaginati per un archivio di sottocategoria
        'products/([^/]+)/([^/]+)/([^/]+)/?$' => 'index.php?post_type=sps-product&product-category=' . $wp_rewrite->preg_index( 2 ) . '&sps-product=' . $wp_rewrite->preg_index( 3 ), // 'products/qualsiasi-carattere/sottocategoria/slug-articolo/'
        'products/([^/]+)/([^/]+)/([^/]+)/([^/]+)/?$' => 'index.php?post_type=sps-product&product-category=' . $wp_rewrite->preg_index( 3 ) . '&sps-product=' . $wp_rewrite->preg_index( 4 ), // 'products/qualsiasi-carattere/sottocategoria/sotto-sottocategoria/slug-articolo/'
    );
    $wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
}

La prossima funzione invece risolve il problema con le sottocategorie. Il problema consiste nel fatto che quando si prova a caricare una pagina con URL faq/categoria/sottocategoria/, WordPress proverebbe a caricare un articolo con slug "sottocategoria" invece della sottocategoria con slug "sottocategoria".

// Un modo approssimativo per aggiungere il supporto a permalink personalizzati flessibili
// C'è un caso in cui le regole di riscrittura da register_kb_rewrite_rules() falliscono:
// Quando visiti la pagina di archivio per una sezione figlio (ad esempio: http://example.com/faq/categoria/sottocategoria)
// Il punto è che in questa situazione, l'URL viene interpretato come un post Knowledgebase con slug "sottocategoria" dalla sezione "categoria"
function fix_product_subcategory_query($query) {
    if ( isset( $query['post_type'] ) && 'sps-product' == $query['post_type'] ) {
        if ( isset( $query['sps-product'] ) && $query['sps-product'] && isset( $query['product-category'] ) && $query['product-category'] ) {
            $query_old = $query;
            // Controlla se si tratta di risultati impaginati (come i risultati di ricerca)
            if ( 'page' == $query['product-category'] ) {
                $query['paged'] = $query['name'];
                unset( $query['product-category'], $query['name'], $query['sps-product'] );
            }
            // Facciamo più leggera la query sul database
            $query['fields'] = 'ids';
            $query['posts_per_page'] = 1;
            // Vediamo se abbiamo risultati o meno
            $_query = new WP_Query( $query );
            if ( ! $_query->posts ) {
                $query = array( 'product-category' => $query['sps-product'] );
                if ( isset( $query_old['product-category'] ) && 'page' == $query_old['product-category'] ) {
                    $query['paged'] = $query_old['name'];
                }
            }
        }
    }
    return $query;
}
add_filter( 'request', 'fix_product_subcategory_query', 10 );

Questa funzione permette a WordPress di gestire %product_category% nella struttura di riscrittura degli slug del tuo custom post type:

function filter_post_type_link($link, $post)
{
    if ($post->post_type != 'sps-product')
        return $link;

    if ($cats = get_the_terms($post->ID, 'product-category'))
    {
        $link = str_replace('%product_category%', get_taxonomy_parents(array_pop($cats)->term_id, 'product-category', false, '/', true), $link); // vedi funzione personalizzata definita sotto
        $link = str_replace('//', '/', $link);
        $link = str_replace('http:/', 'http://', $link);
    }
    return $link;
}
add_filter('post_type_link', 'filter_post_type_link', 10, 2);

Una funzione personalizzata basata su get_category_parents. Ottiene i genitori della tassonomia:

// mia funzione personale per fare ciò che get_category_parents fa per altre tassonomie
function get_taxonomy_parents($id, $taxonomy, $link = false, $separator = '/', $nicename = false, $visited = array()) {    
    $chain = '';   
    $parent = &get_term($id, $taxonomy);

    if (is_wp_error($parent)) {
        return $parent;
    }

    if ($nicename)    
        $name = $parent -> slug;        
else    
        $name = $parent -> name;

    if ($parent -> parent && ($parent -> parent != $parent -> term_id) && !in_array($parent -> parent, $visited)) {    
        $visited[] = $parent -> parent;    
        $chain .= get_taxonomy_parents($parent -> parent, $taxonomy, $link, $separator, $nicename, $visited);

    }

    if ($link) {
        // niente, non riesco a farlo funzionare :(
    } else    
        $chain .= $name . $separator;    
    return $chain;    
}

Fonti:

19 feb 2018 23:27:17
Commenti

Questo codice sembra funzionare, ma causa un paio di problemi quando lo implemento sul mio sito web. Innanzitutto, fa sì che gli articoli e le pagine si interrompano - solo gli elementi sotto un custom post type funzionano effettivamente. In secondo luogo, per qualche motivo il codice fa sì che il template single per il mio custom post type venga completamente ignorato. Ho cercato di capire la causa, ma finora senza successo!

Zach Nicodemous Zach Nicodemous
24 set 2019 22:04:16

Ho risolto il primo problema, ma per quanto riguarda il secondo, la funzione fix_product_subcategory_query() interrompe il template single per il mio custom post type. Nello specifico, carica semplicemente il template generico "single.php" e non quello specifico per il mio CPT. Puoi suggerire una soluzione?

Zach Nicodemous Zach Nicodemous
25 set 2019 03:25:22
0

In realtà, questo è un modo pessimo di procedere.

Se aggiungi la tassonomia prima del custom post type utilizzando il timing di add_action, sarai in grado di usare la regola rewrite del custom post type per prefissare la tassonomia.

es.

    
    // Registra Tassonomia Personalizzata
    function type_taxonomy() {
    
        $labels = array(
            'name'                       => _x( 'Tipi', 'Nome Generale Tassonomia', 'asw' ),
            'singular_name'              => _x( 'Tipo', 'Nome Singolare Tassonomia', 'asw' ),
        );
        $rewrite = array(
            'slug'                       => 'update/tipo',
            'with_front'                 => false,
            'hierarchical'               => false,
        );
        $args = array(
            'labels'                     => $labels,
            'hierarchical'               => true,
            'public'                     => true,
            'show_ui'                    => true,
            'show_admin_column'          => true,
            'show_in_nav_menus'          => true,
            'show_tagcloud'              => true,
            'rewrite'                    => $rewrite,
            'show_in_rest'               => true,
        );
        register_taxonomy( 'type', array( 'updates' ), $args );
    
    }
// Imposta il timing prima del post type
    add_action( 'init', 'type_taxonomy', $timing = 0 );
    
    }
    
    if ( ! function_exists('updates_post_type') ) {
    
    // Registra Custom Post Type
    function updates_post_type() {
    
        $labels = array(
            'name'                  => _x( 'Aggiornamenti', 'Nome Generale Post Type', 'asw' ),
            'singular_name'         => _x( 'Aggiornamento', 'Nome Singolare Post Type', 'asw' ),
        );
        $rewrite = array(
            'slug'                  => 'update',
            'with_front'            => true,
            'pages'                 => true,
            'feeds'                 => true,
        );
        $args = array(
            'label'                 => __( 'Aggiornamenti', 'asw' ),
            'description'           => __( 'I post dovrebbero includere aggiornamenti importanti su salute, benessere e comunità', 'asw' ),
            'labels'                => $labels,
            'supports'              => array( 'title', 'editor', 'thumbnail' ),
            'taxonomies'            => array( 'type', 'category', 'post_tag', 'brands', 'markets' ),
            'hierarchical'          => false,
            'public'                => true,
            'show_ui'               => true,
            'show_in_menu'          => true,
            'menu_position'         => 5,
            'menu_icon'             => 'dashicons-heart',
            'show_in_admin_bar'     => true,
            'show_in_nav_menus'     => true,
            'can_export'            => true,
            'has_archive'           => true,
            'exclude_from_search'   => false,
            'publicly_queryable'    => true,
            'rewrite'               => $rewrite,
            'capability_type'       => 'page',
            'show_in_rest'          => true,
        );
        register_post_type( 'updates', $args );
    
    }
// Imposta il timing dopo la tassonomia 
    add_action( 'init', 'updates_post_type', $timing = 10 );
    
    }
14 apr 2020 13:22:02