How can I use get_post_meta with add_rewrite_rule to build custom permalinks?

I’m trying to change the url structure of my custom post type posts, but It’s not working correctly right now.

What I’m trying to do is change the urls of my custom post type (Test) posts via functions.php. By using post_type_link and add_rewrite_rule I want to rewrite %hallo% to a custom field value (by using get_post_meta). I hope someone can help me out with this. I’ve already tried a few dozen approaches, but it’s still not working.

On the admin side, what I’m having now is working correctly. It shows the urls the way I want them to (domain.com/test/mycustomfieldvalue/post-title/). The problem happens when I want to view the page on the front-end. I get a 400 Bad Request error and I can see my url is changed to domain.com/test/%hallo%/post-title/.

Note: I’m aware of to need to flush my rewrite rules when changing the urls. I’m constantly using flush_rewrite_rules() right now for testing so that can’t be the issue.

Look at my code below.

My custom post type

add_action( 'init', 'cpt_test_init' );

function cpt_test_init() {

    $labels = array(
        'name'               => _x( 'Test', 'post type general name', 'mytheme' ),
        'singular_name'      => _x( 'Test', 'post type singular name', 'mytheme' ),
        'menu_name'          => _x( 'Test', 'admin menu', 'mytheme' ),
        'name_admin_bar'     => _x( 'Test', 'add new on admin bar', 'mytheme' ),
        'add_new'            => _x( 'Nieuwe test toevoegen', 'faq', 'mytheme' ),
        'add_new_item'       => __( 'Nieuwe test toevoegen', 'mytheme' ),
        'new_item'           => __( 'Nieuwe test', 'mytheme' ),
        'edit_item'          => __( 'Test aanpassen', 'mytheme' ),
        'view_item'          => __( 'Bekijk test', 'mytheme' ),
        'all_items'          => __( 'Alle test', 'mytheme' ),
        'search_items'       => __( 'Test zoeken', 'mytheme' ),
        'parent_item_colon'  => __( 'Bovenliggend item:', 'mytheme' ),
        'not_found'          => __( 'Geen test gevonden.', 'mytheme' ),
        'not_found_in_trash' => __( 'Geen test gevonden in de prullenbak.', 'mytheme' )
    );

    $args = array(
        'labels'             => $labels,
        'description'        => __( 'Description.', 'mytheme' ),
        'public'             => true,
        'publicly_queryable' => true,
        'show_ui'            => true,
        'show_in_menu'       => true,
        'query_var'          => true,
        'rewrite'            => array( 'slug' => 'test/%hallo%', 'with_front' => false ),
        'capability_type'    => 'post',
        'has_archive'        => false,
        'hierarchical'       => false,
        'menu_position'      => NULL,
        'supports'           => array( 'title', 'revisions' )
    );

    register_post_type( 'test', $args );
}

How I’m trying to change the urls

function change_test_links( $post_link, $post, $leavename ) {
    if ( is_object( $post ) && $post->post_type == 'test' ) {
        $metaText = get_post_meta(get_the_ID(), 'test_text_field', true);
        if ( $metaText ) {
            return str_replace( '%hallo%', $metaText, $post_link );
        }
    }
    return $post_link;
}
add_filter( 'post_type_link', 'change_test_links', 1, 3 );

function test_rewrite_rules() {
    add_rewrite_rule(
        '^test/(.*)/(.*)/?$',
        'index.php?post_type=test&name=$matches(2)',
        'top'
    );
}
add_action( 'init', 'test_rewrite_rules' );

I really hope someone can help me with this. I feel like I’m close or I’m missing something simple. Thanks in advance!

Edit: When I try to rewrite %hallo% to a hard-coded string (for example ‘mynewtext’) it all does work. So I’m guessing my problem is the use of get_post_meta.

Does work:

return str_replace( '%hallo%', 'mynewtext', $post_link );

Doesn’t work:

$metaText = get_post_meta(get_the_ID(), 'test_text_field', true);
return str_replace( '%hallo%', $metaText, $post_link );