This website is currently in beta. Side effects may occur.

Selecting Children and GrandChildren in ProcessWire

Simple helper functions in PHP to get the children or grandchildren (descendants) of a page in ProcessWire

  • Home
  • Journal
  • Selecting Children and GrandChildren in ProcessWire



The Problem:

My page structure is as follows:

Home

  |- Projects

     |- Nature

        |- Featured Image // Hidden & Unpublished, has a checkbox 'featured', checked

        |- Another Image // Hidden & Unpublished, has a checkbox 'featured', unchecked

        |- Another Image //Hidden & Unpublished, has a checkbox 'featured', unchecked

     |- People

        |- Featured Image // Hidden & Unpublished, has a checkbox 'featured', checked

        |- Another Image // Hidden & Unpublished, has a checkbox 'featured', unchecked

        |- Another Image //Hidden & Unpublished, has a checkbox 'featured', unchecked

        |- Another Image // Hidden & Unpublished, has a checkbox 'featured', unchecked

        |- Another Image //Hidden & Unpublished, has a checkbox 'featured', unchecked

     |- Animals

        |- Featured Image // Hidden & Unpublished, has a checkbox 'featured', checked

        |- Another Image // Hidden & Unpublished, has a checkbox 'featured', unchecked

        |- Another Image //Hidden & Unpublished, has a checkbox 'featured', unchecked

   ......

   and so on ...

   ......

   |- About

   |- Contact

I am having problems displaying recent 3 projects' Featured Images on the home page.

Currently I have 6 pages with featured checkbox checked for testing. The code above displays all 6 instead of limiting to recent 3 sorted by -date..

How can I get recent three to display on my homepage?

The Solution:

A simple function to get the grandchildren of a page (0 to 2 levels deep):

(The code below is just an example, be-careful, it does not filter results and would return pages you would not want but can easily be extended to include filtering)

<?php
/*
 * Function to get 0 to 2 levels deep page children (returns results from 'all' pages)
 * Home (Depth 0)
 *  |-Level 1 (Depth 1)
 *      |-Level 2 (Depth 2)
 */
function getChildren($parent, $depth) {
    
    if ($depth > 2 || $depth < 0) throw new Exception ('Depth invalid. Should be 0 or <=2.');
    
    $include = 'all';
    $parent = wire('pages')->get("{$parent}");
    $level1children = $parent->children("parent={$parent}, include={$include}");
    $level2children = $parent->find("parent={$level1children}, include={$include}");
    
    switch ($depth){
    case '0':
        return array($parent);
        break;
    case '1':
        return $level1children;
        break;
    case '2':
        return $level2children;
        break;
    }
}
?>

Going overboard (0 to 5 levels deep)

<?php
/*
 * Function to get 0 to 5 levels deep page children (returns results from 'all' pages)
 * Home (Depth 0)
 *  |-Level 1 (Depth 1)
 *      |-Level 2 (Depth 2)
 *         |-Level 3 (Depth 3)
 *              |-Level 4 (Depth 4)
 *                  |-Level 5 (Depth 5)
 */
function getChildren($parent, $depth) {
    
    if ($depth > 5 || $depth < 0) throw new Exception ('Depth invalid. Should be 0 or <=5.');
    
    $include = 'all';
    $parent = wire('pages')->get("{$parent}");
    $level1children = $parent->children("parent={$parent}, include={$include}");
    $level2children = $parent->find("parent={$level1children}, include={$include}");
    $level3children = $parent->find("parent={$level2children}, include={$include}");
    $level4children = $parent->find("parent={$level3children}, include={$include}");
    $level5children = $parent->find("parent={$level4children}, include={$include}");
    
    switch ($depth){
    case '0':
        return array($parent);
        break;
    case '1':
        return $level1children;
        break;
    case '2':
        return $level2children;
        break;
    case '3':
        return $level3children;
        break;
    case '4':
        return $level4children;
        break;
    case '5':
        return $level5children;
        break;        
    }
}
?>

The function above can easily be extended to filter results. Hope this would prove useful.

To get GrandChildren of any Page

<?php
/*
 * Function to get grandchildren of any page
 *
 * Required arguments are $parent_page and $template_name
 *
 * NoteToSelf: REMEMBER: This function returns grandchildren of a page a.k.a children of children, so
 * if you specify '/' as parent and 'some_template' as template, it will return an array of 2nd level pages
 * i.e. Home->sub-page->sub-page
 *
 * Argument passing example:
 * For $parent_page pass as '/' for Home/root or '/somepage/' for some other page
 * For $template_name pass as 'template_name'
 * [OPTIONAL] For $include pass as 'all' or 'hidden' etc [read ProcessWire Docs for more]
 * [OPTIONAL] For $sort pass as 'date' or '-date' etc [read ProcessWire Docs for more]
 * [OPTIONAL] For $limit pass as '0' or '10' or '999999999' etc [read ProcessWire Docs for more]
 * [OPTIONAL] For $custom_field_check pass as 'field=value' or 'is_featured=1' etc [read ProcessWire Docs for more]
 *
 * To skip optional arguments pass null
 *
 * Usage example using null:
 *
 *  $gc = getGrandChildren('/', 'my_template', null, '-date', '3', null);
 *      if ($gc) {
 *          foreach ($gc as $child) {
 *              echo $child->title . '<br />' . $child->name . '<br />';
 *          }
 *      }
 *
 * Usage example using all arguments passed to the function:
 *
 *  $gc = getGrandChildren('/projects/', 'photo_page_template', 'all', '-date', '3', 'is_featured=1');
 *      if ($gc) {
 *          foreach ($gc as $child) {
 *              echo $child->title . '<br />' . $child->name . '<br />';
 *          }
 *      }
 *
 * Kay Lohn | https://processwire.com/talk/user/2149-kay-lohn/
 *      
 */
function getGrandChildren($parent_page, $template_name, $include = null, $sort = null, $limit = null, $custom_field_check = null) {
    // Declare variables
    $parent_page = wire('pages')->get("{$parent_page}");
    
    // Initialize variable as a new PageArray()
    $grandchildren = new PageArray();
 
    // Initialize a blank variable to store query
    $query = "";
    
    // Check and process required values
    if (!$template_name) // Throw exception if not present
        throw new Exception('Template name is required');
    if ($template_name) // Add to $query if present
        $query .= 'template=' . $template_name;
    if (!$parent_page) // Throw exception if not present
        throw new Exception('Parent Page is required');
    if ($parent_page) // Add to $query if present
        $query .= ', parent=' . $parent_page->children;
    
    // Check and process Optional values
    if ($include) // Add to $query if present
        $query .= ', include=' . $include;
    if ($sort) // Add to $query if present
        $query .= ', sort=' . $sort;
    if ($limit) // Add to $query if present
        $query .= ', limit=' . $limit;
    if ($custom_field_check) // Add to $query if present
        $query .= ', ' . $custom_field_check;
    
    // Run loop
    foreach (wire('pages')->find($query) as $result) {
        $grandchildren->add($result); // Add each result to PageArray
    }
 
    // Experimental
    if (!empty($grandchildren)) {
        return $grandchildren; // Return the page array
    } else {
        throw new Exception('No results :('); // Throw an error if PageArray was empty
    }
}
?>

The find() function is right there and it can be used directly, but then I'd have to write long long lines each time I need some grandchildren or children or great grandchildren etc.

Compare this:

<?php
$parent_page = wire('pages')->get('/projects/');
foreach ($pages->find("parent={$parent_page->children}, include=all") as $child) { ?>
   echo '<li>'.$child->title.'</li>';
<?php }; ?>

to this:

<?php
    $gc = getChildren('/projects/', '2');
    foreach ($gc as $child) {
        echo '<li>'.$child->title.'</li>';
    }
?>

Untested, contrib by BitPoet: https://processwire.com/talk/user/2900-bitpoet/

function getAncestors($pg, $level)
{
    $retPages = (new PageArray())->add($pg);
    if( $level > 0 )
    {
        foreach( $pg->children As $child )
            $retPages->add(getAncestors($child, $level - 1));
    }
    return $retPages;
}

A sample page structure:

Home

  |- Projects // name=projects

     |- Nature

        |- Featured Image // Hidden & Unpublished, has a checkbox 'featured', checked

        |- Another Image // Hidden & Unpublished, has a checkbox 'featured', unchecked

        |- Another Image //Hidden & Unpublished, has a checkbox 'featured', unchecked

     |- People

        |- Featured Image // Hidden & Unpublished, has a checkbox 'featured', checked

        |- Another Image // Hidden & Unpublished, has a checkbox 'featured', unchecked

  . . .

  |- Events // name=events

     |- Photography 101

        |- Featured Image // Hidden & Unpublished, has a checkbox 'featured', checked

        |- Another Image // Hidden & Unpublished, has a checkbox 'featured', unchecked

        |- Another Image //Hidden & Unpublished, has a checkbox 'featured', unchecked

     |- Advanced Photography

        |- Featured Image // Hidden & Unpublished, has a checkbox 'featured', checked

        |- Another Image // Hidden & Unpublished, has a checkbox 'featured', unchecked

 . . .

https://processwire.com/talk/topic/956-how-to-get-grand-childs/page-2