Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

App_Zend_Navigation_Menu - refactored

By: a guest on Feb 18th, 2011  |  syntax: PHP  |  size: 13.49 KB  |  views: 79  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. <?php
  2. class App_View_Helper_Navigation_Menu extends Zend_View_Helper_Navigation_Menu
  3. {
  4.    
  5.     /**
  6.      * whether parent siblings should be displayed or not
  7.      *
  8.      * @var bool
  9.      */
  10.     protected $_renderParentSiblings = false;
  11.  
  12.  
  13.     /**
  14.      * Returns flag indicating whether parent's siblings should be rendered
  15.      * when rendering
  16.      * only the active branch
  17.      *
  18.      * By default, this value is false.
  19.      *
  20.      * @return bool  whether parent's siblings should be rendered
  21.      */
  22.     public function getRenderParentSiblings()
  23.     {
  24.         return $this->_renderParentSiblings;
  25.     }
  26.  
  27.  
  28.     /**
  29.      * Enables/disables rendering of parent's siblings when only rendering
  30.      * active branch
  31.      *
  32.      * See {@link setOnlyActiveBranch()} for more information.
  33.      *
  34.      * @param  bool $flag                        [optional] render parent's
  35.      *                                           siblings when
  36.      *                                           rendering active branch.
  37.      *                                           Default is false.
  38.      * @return Zend_View_Helper_Navigation_Menu  fluent interface, returns self
  39.      */
  40.     public function setRenderParentSiblings($flag = false)
  41.     {
  42.         $this->_renderParentSiblings = (bool) $flag;
  43.         return $this;
  44.     }
  45.  
  46.  
  47.     /**
  48.      * Normalizes given render options
  49.      *
  50.      * @param  array $options  [optional] options to normalize
  51.      * @return array           normalized options
  52.      */
  53.     protected function _normalizeOptions(array $options = array())
  54.     {
  55.         $options = parent::_normalizeOptions($options);
  56.  
  57.         if (!isset($options['renderParentSiblings'])) {
  58.             $options['renderParentSiblings'] = $this->getRenderParentSiblings();
  59.         }
  60.  
  61.         return $options;
  62.     }
  63.  
  64.  
  65.     /**
  66.      * Renders helper
  67.      *
  68.      * Renders a HTML 'ul' for the given $container. If $container is not given,
  69.      * the container registered in the helper will be used.
  70.      *
  71.      * Available $options:
  72.      *
  73.      *
  74.      * @param  Zend_Navigation_Container $container  [optional] container to
  75.      *                                               create menu from. Default
  76.      *                                               is to use the container
  77.      *                                               retrieved from
  78.      *                                               {@link getContainer()}.
  79.      * @param  array                     $options    [optional] options for
  80.      *                                               controlling rendering
  81.      * @return string                                rendered menu
  82.      */
  83.     public function renderMenu(Zend_Navigation_Container $container = null,
  84.                                array $options = array())
  85.     {
  86.         if (null === $container) {
  87.             $container = $this->getContainer();
  88.         }
  89.  
  90.         $options = $this->_normalizeOptions($options);
  91.  
  92.         if ($options['onlyActiveBranch'] && !$options['renderParents']) {
  93.             $html = $this->_renderDeepestMenu($container,
  94.                                               $options['ulClass'],
  95.                                               $options['indent'],
  96.                                               $options['minDepth'],
  97.                                               $options['maxDepth']);
  98.         } else {
  99.             $html = $this->_renderMenu($container,
  100.                                        $options['ulClass'],
  101.                                        $options['indent'],
  102.                                        $options['minDepth'],
  103.                                        $options['maxDepth'],
  104.                                        $options['onlyActiveBranch'],
  105.                                        $options['renderParentSiblings']);
  106.         }
  107.  
  108.         return $html;
  109.     }
  110.  
  111.  
  112.     /**
  113.      * Renders a menu with all the siblings
  114.      *
  115.      * @param  Zend_Navigation_Container $container   container to render
  116.      * @param  string                    $ulClass     CSS class for first UL
  117.      * @param  string                    $indent      initial indentation
  118.      * @param  int|null                  $minDepth    minimum depth
  119.      * @param  int|null                  $maxDepth    maximum depth
  120.      * @param  bool                      $onlyActive  render only active branch?
  121.      * @param  bool                      $renderParentSiblings  render all
  122.      *                                                          parent siblings
  123.      * @return string
  124.      */
  125.     protected function _renderMenu(Zend_Navigation_Container $container,
  126.                                                $ulClass,
  127.                                                $indent,
  128.                                                $minDepth,
  129.                                                $maxDepth,
  130.                                                $onlyActive,
  131.                                                $renderParentSiblings = false)
  132.     {
  133.         $html = '';
  134.  
  135.         // find deepest active
  136.         if ($found = $this->findActive($container, $minDepth, $maxDepth, true)) {
  137.             $foundPage = $found['page'];
  138.             $foundDepth = $found['depth'];
  139.         } else {
  140.             $foundPage = null;
  141.         }
  142.  
  143.         // create iterator
  144.         $iterator = new RecursiveIteratorIterator($container,
  145.                             RecursiveIteratorIterator::SELF_FIRST);
  146.         if (is_int($maxDepth)) {
  147.             $iterator->setMaxDepth($maxDepth);
  148.         }
  149.        
  150.         if($renderParentSiblings) {
  151.             $siblings = array();
  152.             if($foundPage) {
  153.                 // iterate container
  154.                 // and build siblings array()
  155.                 foreach ($iterator as $page) {
  156.                     $depth = $iterator->getDepth();
  157.                     if($depth >= $minDepth) {
  158.                         if($page->hasPage($foundPage, true)
  159.                                 || $page == $foundPage) {
  160.                             // if $page is $foundpage
  161.                             // or has $foundPage as a child
  162.                             // add all $page siblings to the siblings array
  163.                             if(!is_null($page->getParent())) {
  164.                                 foreach ($page->getParent()->getPages()
  165.                                         as $sibling)
  166.                                 {
  167.                                     $siblings[] = $sibling;
  168.                                 }
  169.                             }
  170.                         }
  171.                     }
  172.                 }
  173.             }
  174.         }
  175.  
  176.         $prevDepth = -1;
  177.         foreach ($iterator as $page) {
  178.             $depth = $iterator->getDepth();
  179.             $isActive = $page->isActive(true);
  180.  
  181.             if($renderParentSiblings && in_array($page, $siblings)
  182.                     && $this->accept($page, true) && $depth >= $minDepth) {
  183.                 // $page is a sibling that should get displayed
  184.                 $accept = true;
  185.  
  186.             } else if ($depth < $minDepth || !$this->accept($page, true)) {
  187.                 // page is below minDepth or not accepted by acl/visibilty
  188.                 continue;
  189.             } else if ($onlyActive && !$isActive) {
  190.                 // page is not active itself, but might be in the active branch
  191.                 $accept = false;
  192.                 if ($foundPage) {
  193.                     if ($foundPage->hasPage($page)) {
  194.                         // accept if page is a direct child of the active page
  195.                         $accept = true;
  196.                     } else if ($foundPage->getParent()->hasPage($page)) {
  197.                         // page is a sibling of the active page...
  198.                         if (!$foundPage->hasPages() ||
  199.                             is_int($maxDepth) && $foundDepth + 1 > $maxDepth) {
  200.                             // accept if active page has no children, or the
  201.                             // children are too deep to be rendered
  202.                             $accept = true;
  203.                         }
  204.                     }
  205.                 }
  206.  
  207.                 if (!$accept) {
  208.                     continue;
  209.                 }
  210.             }
  211.  
  212.             // make sure indentation is correct
  213.             $depth -= $minDepth;
  214.             $myIndent = $indent . str_repeat('        ', $depth);
  215.  
  216.             if ($depth > $prevDepth) {
  217.                 // start new ul tag
  218.                 if ($ulClass && $depth ==  0) {
  219.                     $ulClass = ' class="' . $ulClass . '"';
  220.                 } else {
  221.                     $ulClass = '';
  222.                 }
  223.                 $html .= $myIndent . '<ul' . $ulClass . '>' . self::EOL;
  224.             } else if ($prevDepth > $depth) {
  225.                 // close li/ul tags until we're at current depth
  226.                 for ($i = $prevDepth; $i > $depth; $i--) {
  227.                     $ind = $indent . str_repeat('        ', $i);
  228.                     $html .= $ind . '    </li>' . self::EOL;
  229.                     $html .= $ind . '</ul>' . self::EOL;
  230.                 }
  231.                 // close previous li tag
  232.                 $html .= $myIndent . '    </li>' . self::EOL;
  233.             } else {
  234.                 // close previous li tag
  235.                 $html .= $myIndent . '    </li>' . self::EOL;
  236.             }
  237.  
  238.             // render li tag and page
  239.             $liClass = $isActive ? ' class="active"' : '';
  240.             $html .= $myIndent . '    <li' . $liClass . '>' . self::EOL
  241.                    . $myIndent . '        ' . $this->htmlify($page) . self::EOL;
  242.  
  243.             // store as previous depth for next iteration
  244.             $prevDepth = $depth;
  245.         }
  246.  
  247.         if ($html) {
  248.             // done iterating container; close open ul/li tags
  249.             for ($i = $prevDepth+1; $i > 0; $i--) {
  250.                 $myIndent = $indent . str_repeat('        ', $i-1);
  251.                 $html .= $myIndent . '    </li>' . self::EOL
  252.                        . $myIndent . '</ul>' . self::EOL;
  253.             }
  254.             $html = rtrim($html, self::EOL);
  255.         }
  256.  
  257.         return $html;
  258.     }
  259.    
  260.  
  261.     /**
  262.      * Finds the deepest active page in the given container
  263.      *
  264.      * @param  Zend_Navigation_Container $container  container to search
  265.      * @param  int|null                  $minDepth   [optional] minimum depth
  266.      *                                               required for page to be
  267.      *                                               valid. Default is to use
  268.      *                                               {@link getMinDepth()}. A
  269.      *                                               null value means no minimum
  270.      *                                               depth required.
  271.      * @param  int|null                  $minDepth   [optional] maximum depth
  272.      *                                               a page can have to be
  273.      *                                               valid. Default is to use
  274.      *                                               {@link getMaxDepth()}. A
  275.      *                                               null value means no maximum
  276.      *                                               depth required.
  277.      * @param bool                       $lookForInvisible  if false, invisible
  278.      *                                                      pages will not be
  279.      *                                                      found
  280.      * @return array                                 an associative array with
  281.      *                                               the values 'depth' and
  282.      *                                               'page', or an empty array
  283.      *                                               if not found
  284.      */
  285.     public function findActive(Zend_Navigation_Container $container,
  286.                                $minDepth = null,
  287.                                $maxDepth = -1,
  288.                                $lookForInvisible = false)
  289.     {
  290.         if (!is_int($minDepth)) {
  291.             $minDepth = $this->getMinDepth();
  292.         }
  293.         if ((!is_int($maxDepth) || $maxDepth < 0) && null !== $maxDepth) {
  294.             $maxDepth = $this->getMaxDepth();
  295.         }
  296.  
  297.         $found  = null;
  298.         $foundDepth = -1;
  299.         $iterator = new RecursiveIteratorIterator($container,
  300.                 RecursiveIteratorIterator::CHILD_FIRST);
  301.  
  302.         foreach ($iterator as $page) {
  303.             $currDepth = $iterator->getDepth();
  304.             if ($currDepth < $minDepth
  305.                     || (!$this->accept($page) && $page->isVisible(false)
  306.                             && (!$this->getRenderInvisible()
  307.                             && !$lookForInvisible)))
  308.             {
  309.                 // page is not accepted
  310.                 continue;
  311.             }
  312.  
  313.             if ($page->isActive(false) && $currDepth > $foundDepth) {
  314.                 // found an active page at a deeper level than before
  315.                 $found = $page;
  316.                 $foundDepth = $currDepth;
  317.             }
  318.         }
  319.  
  320.         if (is_int($maxDepth) && $foundDepth > $maxDepth) {
  321.             while ($foundDepth > $maxDepth) {
  322.                 if (--$foundDepth < $minDepth) {
  323.                     $found = null;
  324.                     break;
  325.                 }
  326.  
  327.                 $found = $found->getParent();
  328.                 if (!$found instanceof Zend_Navigation_Page) {
  329.                     $found = null;
  330.                     break;
  331.                 }
  332.             }
  333.         }
  334.  
  335.         if ($found) {
  336.             return array('page' => $found, 'depth' => $foundDepth);
  337.         } else {
  338.             return array();
  339.         }
  340.     }
  341. }