Structură URL pentru Custom Post Type și Taxonomie
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:
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.
Are cineva o soluție pentru această problemă? Îmi smulg părul din cap.
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:

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!

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?

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 );
}
