I solved this way:
Register Custom Post Type:
function custom_post_articles() {
$labels = array(
'name' => _x( 'Articles', 'post type general name', 'textdomain' ),
'singular_name' => _x( 'Article', 'post type singular name', 'textdomain' ),
// etc...
);
$args = array(
'labels' => $labels,
'public' => true,
'menu_position' => 5, // Below Posts.
'menu_icon' => 'dashicons-megaphone', // https://developer.wordpress.org/resource/dashicons/
'capability_type' => 'post',
'hierarchical' => false, // False like post, True like page.
'has_archive' => true, // False like page, True like post.
'rewrite' => array(
'slug' => 'articles/%articles_tax%',
'with_front' => false
),
'supports' => array(
'title',
'editor',
'author',
'thumbnail'
)
);
register_post_type( 'articles', $args );
}
add_action( 'init', 'custom_post_articles' );
Register Custom Taxonomy:
function taxonomies_product() {
$labels = array(
// labels
);
$args = array(
'labels' => $labels,
'public' => true,
'hierarchical' => true, // False like tag, True like category.
'rewrite' => array(
'slug' => 'articles', // Contain lowercase letters and the underscore character (max. 32 char). Default: $taxonomy.
'with_front' => false,
'hierarchical' => true // Allow hierarchical urls.
)
);
register_taxonomy( 'articles_tax', 'articles', $args );
}
add_action( 'init', 'taxonomies_product' );
RewriteRule for Articles URL Structure:
function rewrite_rules_articles( $rules ) {
$new = array();
$new[ 'articles/([^/]+)/(.+)/?$' ] = 'index.php?articles=$matches[2]';
$new[ 'articles/(.+)/?$' ] = 'index.php?articles_tax=$matches[1]';
return array_merge( $new, $rules ); // Put these rules first.
}
add_filter( 'rewrite_rules_array', 'rewrite_rules_articles' );
Replace the placeholder in the URL with the term of the taxonomy:
function handle_placeholder_term_slug( $url, $post ) {
if ( $post->post_type == 'articles' ) {
if ( $cats = get_the_terms( $post->ID, 'articles_tax' ) ) {
$url = str_replace( '%articles_tax%', current( $cats )->slug, $url );
}
}
return $url;
}
add_filter( 'post_type_link', 'handle_placeholder_term_slug', 10, 2 );
Remove the placeholder from the yoast breadcrumbs:
function remove_placeholder_yoast_crumb( $link_output, $link ) {
if ( $link[ 'text' ] == 'Articles' ) {
$link_output = str_replace( '/%articles_tax%', '', $link_output );
}
/* in the case of a multilingual site
if ( in_array( $link[ 'text' ], array( 'Articles', 'Artikel', 'Articoli' ) ) ) {
$link_output = str_replace( '/%articles_tax%', '', $link_output );
}
*/
return $link_output;
}
add_filter( 'wpseo_breadcrumb_single_link', 'remove_placeholder_yoast_crumb', 10 ,2 );