Change permalinks with ACF values

I am running a site where I let users create a profile, which is a custom post type, which is submitted/edited through an ACF front-end form. Everything works as expected, except when users both use the same ‘headline’ (which is sanitized and used as permalink).

I want the permalink to have the following ‘structure’: /post-type/city/{post-title}-{post-id}. My thought was to add a post id, so each link would be unique, but I now found out this is not the case.

If I would have 2 profiles:
www.domain.com/profile/city/i-am-cool-123
www.domain.com/profile/city/i-am-cool-456

Then www.domain.com/profile/city/i-am-cool-456 redirects to www.domain.com/profile/city/i-am-cool-123.

I knew you can’t have 2 the same permalinks, but I might have misunderstood how permalinks are ‘registered’.

Below is my code.

First I’ve added the necessary query vars for the new vars and added custom rewrite tags.

function sd_custom_rewrite_tag() {
    add_rewrite_tag( '%city%', '([^&]+)', 'city=' );
    add_rewrite_tag( '%postname%', '([^&]+)', 'name=" );
}
add_action( "init', 'sd_custom_rewrite_tag', 10, 0 );

function sd_add_query_vars( $vars ) {
    $vars[] = "city";
    $vars[] = "postname";

    return $vars;
}
add_filter( 'query_vars', 'sd_add_query_vars' );

To get the permalink I want, I have the following code in place.

function sd_new_profile_permalink( $permalink, $post, $leavename = false ) {

    if ( strpos( $permalink, '%city%' ) === FALSE ) {
        return $permalink;
    }

    // Get post
    if ( ! $post ) {
        return $permalink;
    }

    // Get custom info
    $city_info  = get_field( 'sd_city_selector', $post->ID );
    $post_slug  = $post->post_name;
    if ( ! is_wp_error( $city_info ) && ! empty( $city_info ) ) {
        $city_replace  = str_replace( '\'', '', $city_info[ 'cityName' ] );
        $city_replace  = str_replace( ' ', '-', $city_replace );
        $city_slug     = strtolower( $city_replace );
        $new_permalink = str_replace( array( '%city%', '%postname%', '%post_id%' ), array( $city_slug, $post_slug, $post->ID ), $permalink );

        return $new_permalink;
    }
    return $permalink;
}
add_filter( 'post_link', 'sd_new_profile_permalink', 10, 3 );
add_filter( 'post_type_link', 'sd_new_profile_permalink', 10, 3 );

So far nothing weird is happening, this is all doing what it’s supposed to do, but now we get down to the issue (I think).

I update the slug through a WPDB action, after the post is submitted, as seen below.

function set_profile_title_from_headline( $post_id ) {

    if ( empty( $_POST[ 'acf' ] ) ) {
        return;
    }

    if ( ! empty( $_POST[ 'acf' ][ 'field_57e3ed6c92ea0' ] ) ) {
        $entered_title = $_POST[ 'acf' ][ 'field_57e3ed6c92ea0' ];
        $cleaned_title = preg_replace( '/[^A-Za-z0-9\-\s]/', '', $entered_title ); 
        $post_name     = sanitize_title( $cleaned_title );
        update_field( 'sd_ad_title', $cleaned_title, $post_id ); 

        global $wpdb;
        $wpdb->update(
            $wpdb->posts,
            array(
                'post_title'  => $cleaned_title,
                'post_name'   => $post_name
            ),
            array(
                'ID' => $post_id
            )
        );

        clean_post_cache( $post_id );

    }

}
add_action( 'acf/save_post', 'set_profile_title_from_headline', 20 );

And then finally I rewrite the url.

function sd_single_profile_rewrite() {
    global $wp_rewrite;
    $wp_rewrite->add_permastruct( 'profile', 'profile/%city%/%postname%-%post_id%/', false );
    add_rewrite_rule( 'profile\/([a-z-]+)\/(.+)-[0-9]+\/?$', 'index.php?post_type=profile&p=$matches[2]&city=$matches[1]&name=$matches[2]', 'top' );
}
add_action( 'init', 'sd_single_profile_rewrite' );

Basically my question is: Is there a way to ‘do’ what I want to do ?
And if so, how 🙂

2 Answers
2

I was thinking too ‘difficult’. Instead of rebuilding a new permalink structure I could have easily updated the new permalink the way I want to have it.

So I deleted the entire rewrite part and changed the query in acf/save_post to the following:

if ( ! empty( $_POST[ 'acf' ][ $ad_title ] ) ) {

    $entered_title = $_POST[ 'acf' ][ $ad_title ];
    $cleaned_title = preg_replace( '/[^A-Za-z0-9\-\s]/', '', 
    $entered_title ); // Removes special chars.
    $post_name     = sanitize_title( $cleaned_title );
    $city_name     = get_field( 'sd_city_search_value', $post_id );
    $new_slug      = strtolower( $city_name ) . '-' . $post_name . '-' . $post_id;

    // update acf field
    update_field( 'sd_ad_title', $cleaned_title, $post_id );

    // update post status + post title (if needed)
    global $wpdb;
    $wpdb->update(
        $wpdb->posts,
        array(
            'post_title'  => $cleaned_title,
            'post_name'   => strtolower( get_field( 'sd_city_search_value', $post_id ) ) . '-' . $post_name . '-' . $post_id
        ),
        array(
            'ID' => $post_id
        )
    );

    clean_post_cache( $post_id );

}

Leave a Comment