First step is to register the post type. The slug doesn’t matter here, we won’t be using any of the rules generated when the post type is registered.
Within this same init
function, we add a rewrite tag and permastruct with our permalink pattern. This will make it easy to construct the permalink in the filter we’ll add later.
The last bit is the rewrite rule that maps incoming requests to the right query vars. To query a custom post type by post ID, you need to set post_type
and p
query vars.
function wpd_test_cpt() {
$args = array(
'label' => 'My CPT',
'public' => true,
'rewrite' => [
'slug' => 'my_cpt',
'with_front' => false,
],
);
register_post_type( 'my_cpt', $args );
add_rewrite_tag( '%foo_or_bar%', '([^/]+)' );
add_permastruct( 'my_cpt', 'fixed-word/%foo_or_bar%/%post_id%/' );
add_rewrite_rule(
'fixed-word/([^/]+)/([0-9]+)/?$',
'index.php?post_type=my_cpt&foo_or_bar=$matches[1]&p=$matches[2]',
'top'
);
}
add_action( 'init', 'wpd_test_cpt' );
The next step is the post_type_link
filter where we swap in values for the placeholders:
function wpd_test_post_type_link( $permalink, $post ) {
if ( 'my_cpt' === $post->post_type ) {
if( $choice = get_field( 'foo_or_bar', $post->ID ) ) {
$permalink = str_replace( ['%foo_or_bar%', '%post_id%'], [$choice, $post->ID], $permalink );
}
}
return $permalink;
}
add_filter( 'post_type_link', 'wpd_test_post_type_link', 10, 2 );
Now this should all work as-is, but you might notice one little quirk- you can change foo
or bar
to whatever you want and the query still succeeds. You can’t refine a singular view by meta data, WordPress sees a post as existing or not solely by ID or slug.
To change this, we can add a bit of code to check if foo_or_bar
is set, and make sure it matches the requested post ID. If it doesn’t match, we redirect to the correct URL:
function wpd_test_pre_get( $query ) {
if ( isset( $query->query['foo_or_bar'] ) && isset( $query->query['p'] ) ) {
if( $choice = get_field( 'foo_or_bar', $query->query['p'] ) ){
if( $choice != $query->query['foo_or_bar'] ){
wp_redirect( get_permalink( $query->query['p'] ) );
}
}
}
}
add_action( 'pre_get_posts', 'wpd_test_pre_get' );