Search code examples
wordpresswordpress-themingcustom-post-typecustom-wordpress-pageswordpress-shortcode

How to use a custom post type slug within another custom post type's slug


I have created two custom post types in my wordpress project: city and property using the below query.

register_post_type('city',
            array(
                'labels' => array(
                    'name' => __('City', 'dc_qode'),
                    'singular_name' => __('City', 'dc_qode'),
                ),
                'public'    =>  true,
                'show_in_menu'  =>  true,
                'rewrite'     =>  array('slug' => 'city'),
                'show_ui'   =>  true,
                'has_archive' =>  false,
                'hierarchical'  =>  true,
                'show_tagcloud' => false,
                'supports' => array(
                    'title',
                    'editor',
                    'thumbnail',
                ),
                'can_export' => true,
                'taxonomies' => array( 'city'),
            )
        );


register_post_type('property',
            array(
                'labels' => array(
                    'name' => __('Property', 'dc_qode'),
                    'singular_name' => __('Property', 'dc_qode'),
                ),
                'public'    =>  true,
                'show_in_menu'  =>  true,
                'rewrite'     =>  array('slug' => 'property'),
                'show_ui'   =>  true,
                'has_archive' =>  false,
                'hierarchical'  =>  true,
                'show_tagcloud' => false,
                'supports' => array(
                    'title',
                    'editor',
                    'thumbnail',
                ),
                'can_export' => true,
                'taxonomies' => array( 'property'),
            )
        );

Using this, I can access any property using the url http://domain-name.com/property/property-name. But I want to access the url as http://domain-name.com/city-name/property-name (for eg. http://domain-name.com/toronto/abcproperty). The city-name will be assigned with each property. I tried to use the slug, 'city', within property as:

'rewrite' => array('slug' => '%city%') 

in place of

'rewrite'     =>  array('slug' => 'property')

But it's not working.

How can I achieve this scenario?


Solution

  • You can make your custom posts to have parents like page. So there are three main thing you have to do:

    1. Make you custom post to support Page attributes.

    2. You have to register new rewrite rule, which will then define a new permastruct which will be used in the third step.

    3. After registering the new rewrite rule, you should apply this rule to your custom posts' permalinks.

      Complete working code

      add_action('init', function(){
          $labels = array(
              "name" => "City",
              "singular_name" => "City",
              "menu_name" => "City",
              "all_items" => "All City",
              "add_new" => "Add New",
              "add_new_item" => "Add New City",
              "edit" => "Edit",
              "edit_item" => "Edit City",
              "new_item" => "New City",
              "view" => "View",
              "view_item" => "View City",
              "search_items" => "Search City",
              "not_found" => "No City Found",
              "not_found_in_trash" => "No City Found in Trash",
              "parent" => "Parent City",
          );
      
          $args = array(
              "labels" => $labels,
               "description" => "",
              "public" => true,
              "show_ui" => true,
              "has_archive" => true,
              "show_in_menu" => true,
              "exclude_from_search" => false,
              "capability_type" => "post",
              "map_meta_cap" => true,
              "hierarchical" => true,
              "rewrite" => array( "slug" => "city", "with_front" => true ),
              "query_var" => true,
              "supports" => array( "title", "revisions", "thumbnail" )
          );
      
          register_post_type( "city", $args );
      
          $labels = array(
              "name" => "Properties",
              "singular_name" => "Property",
          );
      
          $args = array(
              "labels" => $labels,
              "description" => "",
              "public" => true,
              "show_ui" => true,
              "has_archive" => true,
              "show_in_menu" => true,
              "exclude_from_search" => false,
              "capability_type" => "post",
              "map_meta_cap" => true,
              "hierarchical" => false,
              "rewrite" => array( "slug" => "city/%city_name%", "with_front" => true ),
              "query_var" => true,
              "supports" => array( "title", "revisions", "thumbnail" )
          );
      
          register_post_type( "properties", $args );
      
      });
      
      add_action('add_meta_boxes', function() {
          add_meta_box('properties-parent', 'City', 'properties_attributes_meta_box', 'properties', 'side', 'default');
      });
      
      function properties_attributes_meta_box($post) {
              $pages = wp_dropdown_pages(array('post_type' => 'city', 'selected' => $post->post_parent, 'name' => 'parent_id', 'show_option_none' => __('(no parent)'), 'sort_column'=> 'menu_order, post_title', 'echo' => 0));
              if ( ! empty($pages) ) {
                  echo $pages;
              } // end empty pages check
      }
      
      add_action( 'init', function() {
      
          add_rewrite_rule( '^city/(.*)/([^/]+)/?$','index.php?properties=$matches[2]','top' );
      
      });
      
      add_filter( 'post_type_link', function( $link, $post ) {
          if ( 'properties' == get_post_type( $post ) ) {
              //Lets go to get the parent city name
              if( $post->post_parent ) {
                  $parent = get_post( $post->post_parent );
                  if( !empty($parent->post_name) ) {
                      return str_replace( '%city_name%', $parent->post_name, $link );
                  }
              } else {
                  //This seems to not work. It is intented to build pretty permalinks
                  //when properties has not parent, but it seems that it would need
                  //additional rewrite rules
                  //return str_replace( '/%city_name%', '', $link );
              }
      
          }
          return $link;
      }, 10, 2 );