Adding a Taxonomy to Post Type Friendly URL with Same Prefix

This example shows how to create a faq (questions) post type and categories for it (taxonomy faqcat). And the taxonomy will have the same prefix as the post type:

  • Post: `site.com/faq/{category}/{label-record}
  • Taxonomy: site.com/faq/{category}

Here it is important to register taxonomy first, and then the post type:

// register post type and taxonomy
add_action( 'init', 'register_faq_post_type' );

// Filter a friendly URL of post type
add_filter( 'post_type_link', 'faq_permalink', 1, 2 );

function register_faq_post_type() {

	// question category - faqcat
	register_taxonomy( 'faqcat', [ 'faq' ], [
		'label'             => 'Question cat',
		'labels'            => [
			'name'              => 'Question Sections',
			'singular_name'     => 'Question Section',
			'search_items'      => 'Search Question Section',
			'all_items'         => 'All Question Sections',
			'parent_item'       => 'Parenthesis section of the question',
			'parent_item_colon' => 'Parent section of the question:',
			'edit_item'         => 'Edit Question Section',
			'update_item'       => 'Update Question Section',
			'add_new_item'      => 'Add Question Section',
			'new_item_name'     => 'New Question Section',
			'menu_name'         => 'Question Section',
		],
		'description'       => 'Categories for the Questions section',
		'public'            => true,
		'show_in_nav_menus' => false,   // same as public
		'show_ui'           => true,    // same as public
		'show_tagcloud'     => false,   // same as show_ui
		'hierarchical'      => true,
		'rewrite'           => [ 'slug' => 'faq', 'hierarchical' => false, 'with_front' => false, 'feed' => false ],
		// Whether or not to allow auto-creation of a taxonomy column in an associated post type table. (WP 3.5)
		'show_admin_column' => true,
	] );

	// post type - questions - faq
	register_post_type( 'faq', [
		'label'               => 'Questions',
		'labels'              => [
			'name'          => 'Questions',
			'singular_name' => 'Question',
			'menu_name'     => 'Question Archive',
			'all_items'     => 'All Questions',
			'add_new'       => 'Add Question',
			'add_new_item'  => 'Add a new question',
			'edit'          => 'Edit',
			'edit_item'     => 'Edit an issue',
			'new_item'      => 'New question',
		],
		'description'         => '',
		'public'              => true,
		'publicly_queryable'  => true,
		'show_ui'             => true,
		'show_in_rest'        => false,
		'rest_base'           => '',
		'show_in_menu'        => true,
		'exclude_from_search' => false,
		'capability_type'     => 'post',
		'map_meta_cap'        => true,
		'hierarchical'        => false,
		'rewrite'             => [
			'slug'       => 'faq/%faqcat%',
			'with_front' => false,
			'pages'      => false,
			'feeds'      => false,
			'feed'       => false,
		],
		'has_archive'         => 'faq',
		'query_var'           => true,
		'supports'            => [ 'title', 'editor' ],
		'taxonomies'          => [ 'faqcat' ],
	] );
}

function faq_permalink( $permalink, $post ) {

	// stop if it is not our post type: without placeholder %faqcat%
	if( strpos( $permalink, '%faqcat%' ) === false ){
		return $permalink;
	}

	// Get the tax elements
	$terms = get_the_terms( $post, 'faqcat' );
	// change holder if there is element
	if( ! is_wp_error( $terms ) && ! empty( $terms ) && is_object( $terms[0] ) ){
		$term_slug = array_pop( $terms )->slug;
	}
	// There is no element, but there should be...
	else{
		$term_slug = 'no-faqcat';
	}

	return str_replace( '%faqcat%', $term_slug, $permalink );
}

This Note embeded into: register_post_type()