Hierarchical Custom Post Types in Array

I have three custom 'location' post types which make up a hierarchy of areas:

location1, location2, location3.

location1 is the top level and then location2 and location3 are childeren of each other. For example, location1 might be Buildings, location2 Floors, and location3 Rooms.

Read More

location2 and location3 have metaboxes with fields loc2_parent_location and loc3_parent_location respectively. I’m looking to make an array which would populate from custom post types to show like:

Post Type Reference:

location1 > location2 > location3

Echo as:

Building 1
Building 1 > Floor 1
Building 1 > Floor 1 > Room A
Building 1 > Floor 1 > Room B
Building 1 > Floor 2
Building 1 > Floor 3
Building 2

Here is the function I’m working on:

function location_types_query ( $query ) {
// get custom post types
$args = array( 'post_type' => 'location1' );
$args = array( 'post_type' => 'location2' );
$args = array( 'post_type' => 'location3' );

// create hierachy relationships
if ($location2) {
    $parent_location = get_post_meta( $post->ID, 'loc2_parent_location', true );
} elseif ($location3) {
    $parent_location = get_post_meta( $post->ID, 'loc3_parent_location', true );
} elseif ($location1) {
    $parent_location = '';
}

// Run the query
$the_query = new WP_Query( $args );

// The Loop
if ( $the_query->have_posts() ) :
while ( $the_query->have_posts() ) : $the_query->the_post();
  foreach ($location as $locations)

    if (isset($location1)) {
        echo $location1;
    }

    if (isset($location1 && $location2)) {
        echo $location1 . '>' . $location2; 
    }

    if (isset($location1 && $location2  && $location3)) {
        echo $location1 . '>' . $location2 . '>' . $location3; 
    }

    return array($locations);  // output array

endwhile;
endif;

// Reset Post Data
wp_reset_postdata();

}
?>

What should I do to make this function work properly and spit out an array of the fields.

/——————————————————-/
/* Edit 10/23 – Additional Information
/——————————————————-/

@Kaiser – Best way to break this down is as if I am making a real estate property management website. I have the post types like listed above. It’s all dynamic from the options so a user could set up the system to be like above:

Building 1
Building 1 > Floor 1
Building 1 > Floor 1 > Room A

Where “building 1” is a post in cpt location1, “Floor 1” is a post in cpt location2 and “Room A” is a post in cpt location 3.

OR it can be set up like

(location1) > (location2) > (location3) // Post types
Building 1 > Floor 1 > Apartment A // Posts
Building 1 > Floor 1 > Apartment B

Additionally, each post type has a metabox which has values for the details of the location.
For ..

Location 1:
– Location Plan
– Person in charge

Location 2:
– Location Plan
– Person in charge
– Parent Location

Location 3:
– Location Plan
– Person in charge
– Parent Location

I’ve already set up the entire location database for my real estate site. What I now want to do is add a maintenance form on WP’s frontend in a secure member area so tenant’s can submit a maintenance request. I decided to use gravity forms with a multiple select field. I need to populate an array like above to show the different locations in the different buildings, so user can select it and then populate the field like below using the post type array I created for locations:

/*-------------------------------------------------------*/
/* Location
/*-------------------------------------------------------*/
add_filter("gform_pre_render", "gform_prepopluate_populate_location");
add_filter("gform_admin_pre_render", "gform_prepopluate_populate_location");

function gform_prepopluate_populate_location($form){
    $querytype = array('location1', 'location2', 'location3')
    $formid = 5;
    $fieldid = 7;

    if($form["id"] != $formid)
        return $form;

    $posts = query_posts( array( 'post_type' => $querytype ) );
    $items = array(); /* Add Blank */
    $items[] = array("text" => "", "value" => "");

    foreach($posts as $post)
        $items[] = array("value" => $post->ID, "text" => $post->post_title);

    foreach($form["fields"] as &$field)
        if($field["id"] == $fieldid ){            
            $field["choices"] = $items;
        }
    return $form;

}

I can then return the form results as a custom post type, say “maintenance_requests” and have the person doing the work (person responsible) view the request on their tablet and click location which pulls up the plan.

Related posts

2 comments

  1. It seems that you are looking to accomplish is a simple parent/child relationship with *n levels. I ran into something similar when working on subordinate posts https://github.com/codearachnid/wp-subordinate-post. The way I resolved the hierarchy (and even allowed for deep permalinking was to utilize the post_parent field. Based on your statements it seems like there might be several extra associations such as hooking a (maintenance/sales) person to a floor which would cover multiple room etc. However, to answer you main problem is seems that if you tackle the hierarchy that will solve the relationships in the list?

    Thus to spit out the ordered array I’d recommend setting up a looping query for children by parent.

  2. Doing it different

    Your problem mainly is a “XY Problem”. You might make your life a lot easier with simply having three post types, one for each building, or just one named “Rooms”. Then assign the different locations (Building/Floor) to them. You can add descriptions to terms and output them in the right place. Much easier and much more the “native” way, as shared things like the same building or floor should be taxonomy terms. By making them hierarchical you can summon rooms under them.

    In other words, your “Rooms” get assigned a “Floor” and a “Building”. Or even better (if you want to have the possibility to assign floor plans or whatever additional info that is unique to a floor, then you just assign a single floor that has a parent term with the name of the respective Building name as term.

    • Building A >
      • Floor 1A
      • Floor 2A
      • Floor 3A
    • Building B >
      • Floor 1B
      • Floor 2B
      • Floor 3B

    And the rooms are for example

    Custom Post Type “Rooms” > Room Name: “Philip K. Dick Congress Room” | Floor 2B

    and the parent term “Building B” can just get called in the context (it’s already connected to the term.

    Advantage is that you easily can add stuff like: “Are you searching for …”

    • “Other rooms on the same floor”
    • “Other rooms in the same building”
    • etc.

    which might make it easier to correct mistakes if someone is on the right floor but in the wrong building, etc. Endless possibilities.

    Registering the right way

    First take a look at register_taxonomy() and it’s arguments. You’ll notice that you can add a taxonomy that’s hierarchical as the built in “category” taxonomy is:

    register_taxonomy( 'rooms', 
        array(
            'location1',
            'location2',
            'location3',
        ),
        array(
            'hierarchical' => 'true',
            // other arguments
            'rewrite'      => array(
                'slug'         => 'floor',
                'with_front'   => true,
                'hierarchical' => true,
            ),
            'labels'       => array(
                'name'          => __( 'Rooms', 'your_textdomain' ),
                'singular_name' => __( 'Room', 'your_textdomain' ),
                'parent_item'   => __( 'Floor', 'your_textdomain' ),
            ),
        )
    
    );
    

    Important: Make sure you register the taxonomies and the post types to each other right when you register them. Make sure you do so early enough, on init on a priority of 0. More info about that here.

    Doing the tax_query

    Then look at WP_Query parameters.

    You can simply query by multiple post types:

    $locations = new WP_Query( array(
        'post_type' => array(
            'location1',
            'location2',
            'location3',
        ),
        'tax_query' => array(
            'realation' => 'OR',
            array( 
                'taxonomy' => 'rooms',
                'field'    => 'slug',
                'terms'    => array(
                    'roomA',
                    'roomB',
                ),
            ),
        ),
    ) );
    

    The loop and term ancestors

    Then you can loop through your new $location loop. Every time you call a post, you can simply get_ancestors( $termID, $taxonomyName ) to get all ancestors (Building/Floor) for that location.

Comments are closed.