0

There are already few questions on this topic but they are not helping my problem. So what I want to achieve is dynamic CPT permalink structure based on the ACF (radio) value.

// _cb stands for checkbox
radio_acf_cb = foo
radio_acf_cb = bar

Depending on what's the user's choice the permalink would look like this

// fixed_word is the first part of the permalink which will be static
// foo/bar are the values based on the choice from the ACF
// 123 is the post ID 

example.com/fixed_word/foo/123 // if foo is chosen
example.com/fixed_word/bar/123 // if bar is chosen

Here's what I tried so far:

add_filter( 'post_type_link', 'so51217355_post_type_link', 10, 2 );

function so51217355_post_type_link( $permalink, $post ) {

    if ( 'my_cpt' === $post->post_type ) {

        $choice = get_field('foo_bar', $post->ID) // foo or bar

        $choice_slug = $choice == 'foo' ? $choce : 'bar';


        if ( $choice_slug ) {
            // This would give me example.com/fixed_word/foo/123
            // or example.com/fixed_word/bar/123
            $permalink = '/fixed_word/' . $choice_slug . $post->ID;
        }

    }

    return $permalink;
}

Now I know that I need to write some custom rewrite rule with add_rewrite_rule() but I am not sure how to set the conditions.

1
  • There's a typo in your $choice_slug check, you put $choce instead of $choice. Additionally, that field should be backed by a taxonomy/term not post meta for performance reasons, you will want to adjust it accordingly in the ACF field settings
    – Tom J Nowell
    Commented Nov 21, 2018 at 18:03

1 Answer 1

1

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.