0

I have 2 custom post types

Offices

    register_post_type('offices',
        [
            'labels' => [
                'name' => __( 'Offices' ),
                'singular_name' => __( 'Office' )
            ],
            'description' => 'Our Office.',
            'public' => true,
            'hierarchical' => true,
            'has_archive' => false,
            'menu_icon' => 'dashicons-building',
            'support' => ['title', 'custom_fields', 'page-attributes']
        ]
    );

Office members

   //Team Members
    register_post_type( 'office_members',
        [
            'labels' => [
                'name' => __( 'Team Members' ),
                'singular_name' => __( 'Team Member' )
            ],
            'description' => 'Team members for offices.',
            'public' => true,
            'hierarchical' => false,
            'has_archive' => 'offices/([^/]+)/members',
            'show_in_menu' => 'edit.php?post_type=offices',
            'support' => ['title', 'custom_fields', 'page-attributes']
        ]
    );

I want the following urls to work

example.com/offices - Shows the office archive page

example.com/offices/([^/]+) - Show the single office page

example.com/offices/([^/]+)/members - Show the members archive page where parent is the office

example.com/offices/([^/]+)/members/([^/]+) - Show the single member page

I have the following rewrite rules for the office members

    add_permastruct('office_members', '/offices/%office%/members/%office_members%', false,  ['walk_dirs' => false]);
    add_rewrite_tag('%office_members%', '([^/]+)', 'office_members=');
    add_rewrite_rule('^offices/([^/]+)/members/([^/]+)?','index.php?office_members=$matches[2]','top');

All of my urls work except for the members archive page. It loads the template file archive-office_members.php which is great, but it doesn't detect the parent office in the url. So instead of it only showing members for that office it just shows all members.

How can I set my url so that it shows the members archive page, but only shows members based on the office in the url, and so all 4 of my urls work?

1 Answer 1

1

First, you need to register the %office% rewrite tag:

// First, add the rewrite tag.
add_rewrite_tag( '%office%', '([^/]+)', 'post_type=office_members&office_name=' );

// Then call add_permastruct().
add_permastruct( 'office_members', ... );

Then, add the custom office_name arg to the public query vars so that WordPress reads/parses it from the URL:

add_filter( 'query_vars', function ( $vars ) {
    $vars[] = 'office_name';
    return $vars;
} );

And use the pre_get_posts hook to load the correct office_members posts that are children of the offices post with the slug in the office_name arg:

add_action( 'pre_get_posts', function ( $query ) {
    if ( $query->is_main_query() &&
        is_post_type_archive( 'office_members' ) &&
        $slug = $query->get( 'office_name' )
    ) {
        $arg = ( false !== strpos( $slug, '/' ) ) ? 'offices' : 'name';
        $ids = get_posts( "post_type=offices&{$arg}=$slug&fields=ids" );

        if ( ! empty( $ids ) ) {
            $query->set( 'post_parent', $ids[0] );
        }
    }
} );

Now the members archive page should show the proper posts, but we need to fix the pagination for that page, and for that reason, we can use the parse_request hook:

add_action( 'parse_request', function ( $wp ) {
    if ( isset( $wp->query_vars['paged'] ) &&
        preg_match( '#^offices/([^/]+)/members/page/(\d+)#', $wp->request, $matches )
    ) {
        $wp->query_vars['paged']       = $matches[2];
        $wp->query_vars['office_name'] = $matches[1];
    }
} );

And btw, you've got a typo in your register_post_type() args: You used support, but the correct arg name is supports (note the second "s").

Your Answer

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

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