We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 22303 MODX Staff
    • 10,725 Posts
    Here is a quick and dirty NavigateSiblings snippet for simple previous / back / next button rendering. It uses the new $modx->documentMap variable and requires no queries to the database for optimal performance.

    Just create a new snippet, copy and paste the code for the snippet (don’t forget to remove the opening and closing PHP tags, included only so the code is highlighted here in the forum), and save.
    <?php
    /**
     * NavigateSiblings
     * 
     * Flexible prev/next/parent navigation for a document with siblings.  You can
     * use it to output templated links, or just set placeholders
     * 
     * @author Jason Coward, <[email protected]>.
     * @version 1.0
     * 
     * Example usage:
     * 
     * 
     * [!NavigateSiblings? &mode=`direct`!] -- returns the output directly in place
     * of the snippet; it is assumed you are using chunk templates for this mode.
     * This is also the default mode if not specified.
     * 
     * [!NavigateSiblings? &mode=`template`!] -- sets placeholders using template
     * mode by using the default template chunk names, prevSibling, nextSibling, and
     * navSiblingsParent, and producing output to placeholders of the same name as
     * the chunk: navSiblingsPrev, navSiblingsNext, and navSiblingsParent.
     * 
     * [!NavigateSiblings? &mode=`urlonly`!] -- no output is returned, and
     * no templates are processed; instead, the snippet only sets the values of
     * placeholders for each link URL: prevSiblingUrl, nextSiblingUrl, and
     * navSiblingParentUrl.
     */
    
    /*
     * Define the mode of the snippet:
     * 
     * &mode=`template` -- will produce placeholders containing templates within
     * which the URL placeholders can be used (default mode).
     * 
     * &mode=`direct` -- will output the template chunks in the place where the
     * snippet is called within your content.
     *  
     * &mode=`urlonly` -- will only produce placeholders for the navigation URLs.
     * 
     */
    if (!isset ($mode) || ($mode !== 'template') || $mode !== 'urlonly') {
        $mode= 'direct';
    }
    
    /*
     * Define a chunk representing the presentation of the previous sibling link.
     * &prevChunk=`myPrevSiblingChunkName`
     */ 
    if (!isset ($prevChunk)) {
        $prevChunk= 'prevSibling';
    }
    
    /*
     * Define a chunk representing the presentation of the next sibling link.
     * &nextChunk=`myNextSiblingChunkName`
     */
    if (!isset ($nextChunk)) {
        $nextChunk= 'nextSibling';
    }
    
    /*
     * Determine whether or not to show a link back to the parent.
     * &showParent=`0` -- will disable this, which is set to 1 by default.
     */
    if (!isset ($showParent)) {
        $showParent= 1;
    }
    
    /*
     * Define a chunk representing the presentation of the parent link.
     * &parentChunk=`myNavParentChunkName`
     */
    if (!isset ($parentChunk)) {
        $parentChunk= 'navSiblingsParent';
    }
    
    $output= '';
    $prevOut= '';
    $parentOut= '';
    $nextOut= '';
    $parentId= (string) $modx->documentObject['parent'];
    if (!function_exists('filterNonSiblingsFromDocumentMap')) {
        function filterNonSiblingsFromDocumentMap($var) {
            global $modx;
            return (isset ($var[$modx->documentObject['parent']]));
        }
    }
    $siblings= array_filter($modx->documentMap, 'filterNonSiblingsFromDocumentMap');
    $currentKey= false;
    $prevSibling= false;
    $nextSibling= false;
    $firstSibling= current($siblings);
    $currentSibling= $firstSibling;
    while ($currentSibling !== false) {
        if (intval($currentSibling[$parentId]) === intval($modx->documentIdentifier)) {
            $currentKey= key($siblings);
            break;
        }
        $currentSibling= next($siblings);
    }
    $lastSibling= end($siblings);
    if ($currentKey) {
        if (intval($firstSibling[$parentId]) !== intval($modx->documentIdentifier)) {
            $modx->setPlaceholder('prevSiblingUrl', $modx->makeUrl($siblings[$currentKey - 1][$parentId]));
            $prevOut= !empty ($prevChunk) ? $modx->getChunk($prevChunk) : '';
        }
        if ($showParent) {
            $modx->setPlaceholder('navSiblingParentUrl', $modx->makeUrl($parentId));
            $parentOut= !empty ($parentChunk) ? $modx->getChunk($parentChunk) : '';
        }
        if (intval($lastSibling[$parentId]) !== intval($modx->documentIdentifier)) {
            $modx->setPlaceholder('nextSiblingUrl', $modx->makeUrl($siblings[$currentKey + 1][$parentId]));
            $nextOut= !empty ($nextChunk) ? $modx->getChunk($nextChunk) : '';
        }
    }
    if ($mode == 'template') {
        if (!empty ($prevChunk)) {
            $this->setPlaceholder($prevChunk, $prevOut);
        }
        if (!empty ($nextChunk)) {
            $this->setPlaceholder($nextChunk, $nextOut);
        }
        if (!empty ($parentChunk)) {
            $this->setPlaceholder($parentChunk, $parentOut);
        }
    } elseif ($mode == 'direct') {
        $output.= $prevOut . "\n";
        $output.= $parentOut . "\n";
        $output.= $nextOut . "\n";
    }
    return $output;
    ?>


    Then create three chunks named prevSibling, nextSibling, and navSiblingsParent similar to the following:
    <a href="[+prevSiblingUrl+]" title="Go to previous page">Prev</a>

    <a href="[+nextSiblingUrl+]" title="Go to next page">Next</a>

    <a href="[+navSiblingsParentUrl+]" title="Go to the parent page">Index</a>


    That’s it; this should work generically for navigation amongst any set of child documents in a MODx folder.

    NOTE: The $modx->documentMap uses sorting based on menuindex only, so this will only work for navigating amongst siblings when you expect the order based on menuindex.
      • 10487 MODX Staff
      • 1,535 Posts
      Very, very nice! cool

      This will definitely come in handy, thanks Jason!
        Garry Nutting
        Senior Developer
        MODX, LLC

        Email: [email protected]
        Twitter: @garryn
        Web: modx.com
        • 33175
        • 711 Posts
        Thanks Jason !
        I will try it as soon as possible smiley
          Sorry for my english. I&#39;m french... My dictionary is near me, but it&#39;s only a dictionary !
          • 30223
          • 1,010 Posts
          Nice snippet! However be aware that pages not listed in the menu (as for instance the 404 error page) will show up in the previous / next links! Just so you know...

          Also Jason, wouldn’t the below function be a prime candidate for PHP’s create_function()? Instead you’d basically hae a one-liner doing the same thing and no need to test if the function exists.

          <?php
          if (!function_exists('filterNonSiblingsFromDocumentMap')) {
              function filterNonSiblingsFromDocumentMap($var) {
                  global $modx;
                  return (isset ($var[$modx->documentObject['parent']]));
              }
          }
          
          
          $siblings= array_filter($modx->documentMap, 'filterNonSiblingsFromDocumentMap');
          ?>
          
          


          The one-liner (split up for readability)
          <?php
          $siblings= array_filter($modx->documentMap, 
              create_function(  '$var',  'global $modx; return (isset ($var[$modx->documentObject["parent"]]));' )
          );
          ?>
            • 22303 MODX Staff
            • 10,725 Posts
            Quote from: TobyL at May 30, 2006, 10:30 AM

            Nice snippet! However be aware that pages not listed in the menu (as for instance the 404 error page) will show up in the previous / next links! Just so you know...
            Good point TobyL, and thanks for pointing that out. Support for that could probably be added with minimal query work. I’ll see about that when I get a chance.

            Quote from: TobyL at May 30, 2006, 10:30 AM

            Also Jason, wouldn’t the below function be a prime candidate for PHP’s create_function()? Instead you’d basically hae a one-liner doing the same thing and no need to test if the function exists.
            Hmmm, IMO, I’d rather check for it’s existence in this case and simply reuse the existing function definition if this gets called multiple times on a page, rather than adding the overhead of registering a new function with the PHP interpreter on every instance. Not that it would be used more than twice per page in any situation I can think of, but. I’ll research this a little more later on and see what the real benefits and drawbacks of each approach are.
              • 22221
              • 283 Posts
              Nice,

              If you want you can update my similary snippet (PrevJumpNext) or used it to add more features. It is very configurable.

              http://modxcms.com/forums/index.php/topic,4300.0.html
                • 10313
                • 375 Posts
                Hey there, I just found out, that this snippet does exactly what I need. First, I didn’t see the "template" function which just sets placeholders for my needs. Awesome.

                But nevertheless, I have a problem (or two).

                First is that the "mode" parameter is not recognized. I’m using
                [[NavigateSibling? &mode=`template`]]

                from within a chunk. I hardcoded the default mode to "template" instead of "direct". Now this works.

                The next problem is probably not directly one of this snippet. I have my templates set so that they show a little button with an arrow. One on the left side, then a list of all siblings as numbers (generated by Ditto), and on the reight side a button for the next sibling.

                To avoid "jumping" sibling numbers when there is no next sibling (that means the "next button" is not shown), I want to output the same button without a link and dimmed grey. To achieve this, I’m using PHx:

                [+nextSibling:ifempty=`[+nextButton+]`+]

                PHx is correctly installed and works in other places of my site. But here, the content of the "empty" TV is not output. So I suspect the placeholder to not be empty but unset. May this be so?

                Thanks
                Martin
                  • 22303 MODX Staff
                  • 10,725 Posts
                  Quote from: FuryDE at May 26, 2009, 11:15 AM

                  To avoid "jumping" sibling numbers when there is no next sibling (that means the "next button" is not shown), I want to output the same button without a link and dimmed grey. To achieve this, I’m using PHx:

                  [+nextSibling:ifempty=`[+nextButton+]`+]

                  PHx is correctly installed and works in other places of my site. But here, the content of the "empty" TV is not output. So I suspect the placeholder to not be empty but unset. May this be so?
                  That looks like the problem indeed, so instead of returning a real blank value, the placeholder is left for later processing, and so is not empty in the eyes of that PHx statement. You could try making the default values in the snippet code for each of the values to be set as placeholders with an HTML comment, e.g.
                  <!-- Empty previous value -->
                  and look for that comment string in your PHx logic.
                    • 10313
                    • 375 Posts
                    Okay, thank you for your rating (right word). Maybe it is already enough if I set the placeholders with empty strings, so they are defined, but empty. I’ll give it a try.

                    Martin
                      • 10313
                      • 375 Posts
                      Oh my Gosh, I am so dumb and blind. The problem was not to check, if the returned placeholder is set at all (btw: it IS set, even if there is no previous sibling). The problem ist my chunk code:
                      [+nextSibling:ifempty=`[+nextButton+]`+]


                      This "nextButton" is not a placeholder but a chunk, so I just have to use
                      [+nextSibling:ifempty=`{{nextButton}}`+]

                      And all works very fine. I mixed it all up rolleyes

                      Thank you
                      Martin