Skip to content
mgburns edited this page Mar 10, 2013 · 18 revisions

The navigation library provides several low-level functions for querying large amounts of posts hierarchically.

Two functions in particuar are used throughout the navigation plugin to selectively fetch posts in specific sections. (Any post with at least one child is considered a section).

  • bu_navigation_gather_sections() - Load ancestors or descendants of a specific post
  • bu_navigation_get_pages() - Alternative to WP_Query, allows you to get posts in specific sections

Using the navigation library

For example, let's consider the following page hierarchy:

  • About
    • Our staff
      • John Smith
      • Jane Doe
    • Contact Us
    • Our Mission

Assuming that the post ID for the "About" page is 4, the following lines of code would fetch all pages in the "About" section:

<?php
$section_args = array(
    'post_types' => 'page',
    'direction' => 'down'
);
$sections = bu_navigation_gather_sections( 4, $section_args );

$query_args = array(
    'post_types' => 'page',
    'sections' => $sections
);
$pages = bu_navigation_get_pages( $query_args );

$pages_by_parent = bu_navigation_pages_by_parent( $pages );

Breaking this example down -- the call to bu_navigation_gather_sections() will return an array of post ID's, one for each section below post ID 4. This information is then passed to bu_navigation_get_pages() using the "sections" argument; this call builds the query accordingly and returns a flat list of posts contained in the requested sections. The last call to bu_navigation_pages_by_parent() rearranges the flat post list in to a more useful structure - an associative array of posts, grouped in to sections by parent ID.

Note that despite the function name, bu_navigation_get_pages() fully supports custom post types.

Filters

One of the benefits of using WP_Query is that you get post meta and other post-related data (such as taxonomy) queried and cached in memory automatically. Since the navigation library was designed to be a leaner alternative in order to work with large page counts, no extra data is queried and cached with bu_navigation_get_pages(). It is up to clients of the navigation library to fetch any extra data that is needed explicitly.

The navigation library provides several filters which can be used to hook in and modify default behaviors.

Among the most useful filters are the following two found in bu_navigation_get_pages():

  • bu_navigation_filter_fields - Modify which post columns will be SELECT'd from the database
  • bu_navigation_filter_pages - Modify array of post objects returned from the database

Example Usage

The following example, taken from the navigation plugin, uses the bu_navigation_filter_pages filter to fetch multiple custom post meta values in one query and append the results to each post object. In this example the meta data is the custom navigation label, entered using the text field in the "Placement in Navigation" meta box.

<?php
// Name of meta_key used to hold navigation labels
define('BU_NAV_META_PAGE_LABEL', '_bu_cms_navigation_page_label');

function bu_navigation_filter_pages_navlabels( $pages ) {
    global $wpdb;

    $filtered = array();

    if ( is_array( $pages ) && count( $pages ) > 0 ) {

        $ids = array_keys( $pages );
        $query = sprintf( "SELECT post_id, meta_value FROM %s WHERE meta_key = '%s' AND post_id IN (%s) AND meta_value != ''",
            $wpdb->postmeta,
            BU_NAV_META_PAGE_LABEL,
            implode( ',', $ids )
            );
        $labels = $wpdb->get_results( $query, OBJECT_K );

        if ( is_array( $labels ) && count( $labels ) > 0 ) {
            foreach ( $pages as $page ) {
                if ( array_key_exists( $page->ID, $labels ) ) {
                    $label = $labels[ $page->ID ];
                    $page->navigation_label = $label->meta_value;
                }
                $filtered[ $page->ID ] = $page;
            }
        } else {
            $filtered = $pages;
        }
    }

    return $filtered;
}

add_filter( 'bu_navigation_filter_pages', 'bu_navigation_filter_pages_navlabels' );

Adding this filter before a call to bu_navigation_get_pages() will result in posts being returned with navigation labels appended to the post objects themselves (in the navigation_label object property).

Another example uses the same filter to remove posts entirely that have been marked as hidden from navigation lists. (By unchecking the "Display in navigation lists" checkbox in the "Placement in Navigation" meta box).

<?php
// Name of meta_key used to exclude pages from navigation
define( 'BU_NAV_META_PAGE_EXCLUDE', '_bu_cms_navigation_exclude' );

function bu_navigation_filter_pages_exclude( $pages ) {
    global $wpdb;

    $filtered = array();

    if ( is_array( $pages ) && count( $pages ) > 0 ) {

        $ids = array_keys( $pages );
        $query = sprintf( "SELECT post_id, meta_value FROM %s WHERE meta_key = '%s' AND post_id IN (%s) AND meta_value != '0'",
            $wpdb->postmeta,
            BU_NAV_META_PAGE_EXCLUDE,
            implode( ',', $ids )
            );
        $exclusions = $wpdb->get_results( $query, OBJECT_K );

        if ( is_array( $exclusions ) && count( $exclusions ) > 0 ) {
            foreach ( $pages as $page ) {
                if ( ! array_key_exists( $page->ID, $exclusions ) ) {
                    $filtered[ $page->ID ] = $page;
                }
            }
        } else {
            $filtered = $pages;
        }
    }

    return $filtered;
}

add_filter( 'bu_navigation_filter_pages', 'bu_navigation_filter_pages_exclude' );