Structură URL pentru Custom Post Type și Taxonomie

13 feb. 2018, 21:33:31
Vizualizări: 23.6K
Voturi: 8

Am un tip de postare personalizat products și o taxonomie personalizată 'Categorii Produse' cu slug-ul products.

Obiectivul meu: Încerc să creez o structură URL astfel:

Arhivă Tip Postare Produs: products/

Arhive Categorii Produse de Nivel Superior: products/product-category

Arhive Categorii Produse: products/product-category/product-sub-category

Pagină Produs: products/product-category/product-sub-category/product-page

Problema: Paginile produselor par să folosească doar categoriile efective. Astfel, structura URL arată ca products/uncategorized/product-page. Paginile de arhivă pentru categorii nu mențin baza cpt. Astfel, rezultă ceva de genul category/sub-category.

Ce am încercat: Numeroase căutări pe Google, mai multe fragmente de cod (cele pe care mi le amintesc le voi include mai jos), câteva funcții personalizate și câteva plugin-uri. Fără succes.

Am următorul cod.

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

//Un tip de postare personalizat pentru toate produsele
function products_cpt(){
    $labels = array(
        'name'               => _x('Produse', 'Nume General Tip Postare'),
        'singular_name'      => _x('Produs', 'Nume Singular Tip Postare'),
        'menu_name'          => __('Produse'),
        'parent_item_colon'  => __('Produs Părinte'),
        'all_items'          => __('Toate Produsele'),
        'view_item'          => __('Vezi Produs'),
        'add_new_item'       => __('Adaugă Produs Nou'),
        'add_new'            => __('Adaugă Nou'),
        'edit_item'          => __('Editează Produs'),
        'update_item'        => __('Actualizează Produs'),
        'search_items'       => __('Caută Produse'),
        'not_found'          => __('Nu a fost găsit'),
        'not_found_in_trash' => __('Nu a fost găsit în Coș')
    );
    $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(
            'Produs',
            'Produse'
        ),
    );
    foreach($taxonomies as $slug => $name){
        create_product_taxonomy($slug,$name[0],$name[1]);
    }
}

function create_product_taxonomy($slug, $singular, $plural) {
    $labels = array(
        'name'                       => _x( $singular.' Categorii', 'Nume General Taxonomie', 'text_domain' ),
        'singular_name'              => _x( $singular.' Categorie', 'Nume Singular Taxonomie', 'text_domain' ),
        'menu_name'                  => __( $singular.' Categorii', 'text_domain' ),
        'all_items'                  => __( 'Toate Categoriile '.$singular, 'text_domain' ),
        'parent_item'                => __( 'Categorie Părinte '.$singular, 'text_domain' ),
        'parent_item_colon'          => __( 'Categorie Părinte '.$singular.':', 'text_domain' ),
        'new_item_name'              => __( 'Nume Nouă Categorie '.$singular, 'text_domain' ),
        'add_new_item'               => __( 'Adaugă Categorie Nouă '.$singular, 'text_domain' ),
        'edit_item'                  => __( 'Editează Categorie '.$singular, 'text_domain' ),
        'update_item'                => __( 'Actualizează Categorie '.$singular, 'text_domain' ),
        'view_item'                  => __( 'Vezi Categorie '.$singular, 'text_domain' ),
        'separate_items_with_commas' => __( 'Separă Categoriile '.$singular.' cu virgule', 'text_domain' ),
        'add_or_remove_items'        => __( 'Adaugă sau șterge Categorii '.$singular, 'text_domain' ),
        'choose_from_most_used'      => __( 'Alege dintre cele mai folosite Categorii '.$singular, 'text_domain' ),
        'popular_items'              => __( 'Categorii Populare '.$singular, 'text_domain' ),
        'search_items'               => __( 'Caută Categorii '.$singular, 'text_domain' ),
        'not_found'                  => __( 'Nu a fost găsit', 'text_domain' ),
        'no_terms'                   => __( 'Nu există Categorii '.$singular, 'text_domain' ),
        'items_list'                 => __( 'Listă Categorii '.$singular, 'text_domain' ),
        'items_list_navigation'      => __( 'Navigare Listă Categorii '.$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 );
}

În primul rând, am setat setările pentru permalink-uri astfel: Setări Permalink-uri

Apoi, am încercat să adaug următorul cod, însă a funcționat doar pentru paginile de produse. Arhivele Categoriilor de Produse încă returnau eroarea 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;  
}

Și aceasta la funcția de inițializare CPT:

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

Am încercat și următorul cod, nu-mi amintesc ce s-a întâmplat dar știu că nu a funcționat:

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

Am încercat și să mă joc cu filtrul term_link. Fără succes.

În final, am încercat următoarele Plugin-uri, dar tot fără succes.

Custom Permalinks

Permalinks Customizer

Custom Post Type Permalinks

Are cineva o soluție pentru această problemă? Îmi smulg părul din cap.

2
Comentarii

Ai putea avea vreodată products/product-category/pagina-de-produs?

MikeSchinkel MikeSchinkel
15 feb. 2018 04:51:21

@MikeSchinkel Presupun că da, dacă singura categorie selectată era o categorie fără categorii părinte

J Robz J Robz
18 feb. 2018 00:21:35
Toate răspunsurile la întrebare 2
2

După o eternitate, am găsit în sfârșit un răspuns!

În primul rând: înregistrăm tipul de postare personalizat și taxonomia personalizată:

add_action( 'init', 'register_sps_products_post_type' );
function register_sps_products_post_type() {
    register_post_type( 'sps-product',
        array(
            'labels' => array(
                'name' => 'Produse',
                'menu_name' => 'Manager Produse',
                'singular_name' => 'Produs',
                'all_items' => 'Toate Produsele'
            ),
            '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' => 'produse',
            'taxonomies' => array('product-category'),
            'rewrite' => array( 'slug' => 'produse/%product_category%', 'hierarchical' => true, 'with_front' => false )
        )
    );
    register_taxonomy( 'product-category', array( 'sps-product' ),
        array(
            'labels' => array(
                'name' => 'Categorii de Produse',
                'menu_name' => 'Categorii de Produse',
                'singular_name' => 'Categorie de Produse',
                'all_items' => 'Toate Categoriile'
            ),
            'public' => true,
            'hierarchical' => true,
            'show_ui' => true,
            'rewrite' => array( 'slug' => 'produse', 'hierarchical' => true, 'with_front' => false ),
        )
    );
}

Apoi, adăugăm o nouă regulă de rescriere, astfel încât WordPress să știe cum să interpreteze noua structură de permalinkuri:

add_action( 'generate_rewrite_rules', 'register_product_rewrite_rules' );
function register_product_rewrite_rules( $wp_rewrite ) {
    $new_rules = array( 
        'produse/([^/]+)/?$' => 'index.php?product-category=' . $wp_rewrite->preg_index( 1 ), // 'produse/orice-caracter/'
        'produse/([^/]+)/([^/]+)/?$' => 'index.php?post_type=sps-product&product-category=' . $wp_rewrite->preg_index( 1 ) . '&sps-product=' . $wp_rewrite->preg_index( 2 ), // 'produse/orice-caracter/slug-post/'
        'produse/([^/]+)/([^/]+)/page/(\d{1,})/?$' => 'index.php?post_type=sps-product&product-category=' . $wp_rewrite->preg_index( 1 ) . '&paged=' . $wp_rewrite->preg_index( 3 ), // potrivire rezultate paginate pentru o arhivă de sub-categorie
        'produse/([^/]+)/([^/]+)/([^/]+)/?$' => 'index.php?post_type=sps-product&product-category=' . $wp_rewrite->preg_index( 2 ) . '&sps-product=' . $wp_rewrite->preg_index( 3 ), // 'produse/orice-caracter/sub-categorie/slug-post/'
        'produse/([^/]+)/([^/]+)/([^/]+)/([^/]+)/?$' => 'index.php?post_type=sps-product&product-category=' . $wp_rewrite->preg_index( 3 ) . '&sps-product=' . $wp_rewrite->preg_index( 4 ), // 'produse/orice-caracter/sub-categorie/sub-sub-categorie/slug-post/'
    );
    $wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
}

Următoarea funcție remediază problema cu sub-categoriile. Problema constă în faptul că, atunci când încercați să încărcați o pagină cu URL-ul faq/categorie/sub-categorie/, WordPress ar încerca să încarce un post cu slug-ul "sub-categorie" în loc de sub-categoria cu slug-ul "sub-categorie".

// O metodă ingenioasă de a adăuga suport pentru permalinkuri personalizate flexibile
// Există un caz în care regulile de rescriere din register_kb_rewrite_rules() eșuează:
// Când vizitați pagina de arhivă pentru o secțiune copil (de exemplu: http://example.com/faq/categorie/sub-categorie)
// Problema este că în această situație, URL-ul este interpretat ca un post Knowledgebase cu slug-ul "sub-categorie" din secțiunea "categorie"
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;
            // Verifică dacă acesta este un rezultat paginat (cum ar fi rezultatele căutării)
            if ( 'page' == $query['product-category'] ) {
                $query['paged'] = $query['name'];
                unset( $query['product-category'], $query['name'], $query['sps-product'] );
            }
            // Ușurează lucrurile pentru baza de date
            $query['fields'] = 'ids';
            $query['posts_per_page'] = 1;
            // Verifică dacă avem rezultate sau nu
            $_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 );

Această funcție permite WordPress să știe cum să gestioneze %product_category% în structura de rescriere a slug-ului pentru tipul de postare personalizat:

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); // vezi funcția personalizată definită mai jos
        $link = str_replace('//', '/', $link);
        $link = str_replace('http:/', 'http://', $link);
    }
    return $link;
}
add_filter('post_type_link', 'filter_post_type_link', 10, 2);

O funcție personalizată bazată pe get_category_parents. Aceasta obține părinții taxonomiei:

// propria mea funcție pentru a face ceea ce face get_category_parents pentru alte taxonomii
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) {
        // nimic, nu pot face asta să funcționeze :(
    } else    
        $chain .= $name . $separator;    
    return $chain;    
}

Surse:

19 feb. 2018 23:27:17
Comentarii

Acest cod pare să funcționeze, dar cauzează câteva probleme când îl implementez pe site-ul meu. În primul rând, face ca Postările și Paginile să nu funcționeze corect - doar elementele dintr-un tip de postare personalizată funcționează. În al doilea rând, din nu știu ce motiv, codul face ca șablonul single pentru tipul meu de postare personalizată să fie ignorat complet. Am încercat să identific cauza, dar încă fără succes!

Zach Nicodemous Zach Nicodemous
24 sept. 2019 22:04:16

Am rezolvat prima problemă, dar în ceea ce privește a doua problemă, funcția fix_product_subcategory_query() face ca șablonul single pentru tipul meu de postare personalizată să nu funcționeze corect. Mai exact, încarcă doar șablonul generic "single.php" și nu cel specific pentru CPT-ul meu. Poți oferi vreo soluție?

Zach Nicodemous Zach Nicodemous
25 sept. 2019 03:25:22
0

De fapt, acesta este un mod oribil de a face asta.

Dacă adaugi taxonomia înaintea tipului de postare personalizat folosind momentul add_action, vei putea folosi regula de rescriere a tipului de postare personalizat pentru a prefixa taxonomia.

ex.

    
    // Înregistrează Taxonomia Personalizată
    function type_taxonomy() {
    
        $labels = array(
            'name'                       => _x( 'Tipuri', 'Numele General al Taxonomiei', 'asw' ),
            'singular_name'              => _x( 'Tip', 'Numele Singular al Taxonomiei', 'asw' ),
        );
        $rewrite = array(
            'slug'                       => 'update/tip',
            '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 );
    
    }
// Setează momentul înaintea tipului de postare
    add_action( 'init', 'type_taxonomy', $timing = 0 );
    
    }
    
    if ( ! function_exists('updates_post_type') ) {
    
    // Înregistrează Tipul de Postare Personalizat
    function updates_post_type() {
    
        $labels = array(
            'name'                  => _x( 'Actualizări', 'Numele General al Tipului de Postare', 'asw' ),
            'singular_name'         => _x( 'Actualizare', 'Numele Singular al Tipului de Postare', 'asw' ),
        );
        $rewrite = array(
            'slug'                  => 'update',
            'with_front'            => true,
            'pages'                 => true,
            'feeds'                 => true,
        );
        $args = array(
            'label'                 => __( 'Actualizări', 'asw' ),
            'description'           => __( 'Postările ar trebui să includă actualizări importante despre sănătate, bunăstare și comunitate', '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 );
    
    }
// Setează momentul după taxonomie 
    add_action( 'init', 'updates_post_type', $timing = 10 );
    
    }
14 apr. 2020 13:22:02