Search code examples
phpzend-frameworkrecursionzend-navigation

Zend Navigation (Container) - removePage() is not Recursive


Zend_Navigation extends Zend_Navigation_Container. The findOneBy(), findAllBy(), and findBy() functions all search for pages recursively, but removePage() does not. This means that $navigation->removePage($navigation->findOneBy('id', 'page_10')); will work ONLY if page_10 is a root-level navigation node. Has anyone else encountered this and found a work-around?


I have found my own solutions and accepted one of them as how I have implemented it. I will select a solution from someone else if it's better than mine.


Solution

  • Extend Zend_Navigation and Zend_Navigation_Container to recursively remove pages.

    Create My_Navigation_Container that extends Zend_Navigation_Container:

    abstract class My_Navigation_Container extends Zend_Navigation_Container
    {
        /**
         * Remove page(s) matching $property == $value
         *
         * @param string $property
         * @param mixed $value
         * @param bool $all
         * @return My_Navigation_Container
         */
        public function removeBy($property, $value, $all = false)
        {
            $pages = array();
    
            if ($all) {
                $pages = $this->findAllBy($property, $value);
            } else {
                if ($page = $this->findOneBy($property, $value)) {
                    $pages[] = $page;
                }
            }
    
            foreach ($pages as $page) {
                $this->removePageRecursive($page);
            }
    
            return $this;
        }
    
    
        /**
         * Recursively removes the given page from the container
         *
         * @param Zend_Navigation_Page $page
         * @return boolean
         */
        public function removePageRecursive(Zend_Navigation_Page $page)
        {
            if ($this->removePage($page)) {
                return true;
            }
    
            $iterator = new RecursiveIteratorIterator($this, RecursiveIteratorIterator::SELF_FIRST);
            foreach ($iterator as $pageContainer) {
                if ($pageContainer->removePage($page)) {
                    return true;
                }
            }
    
            return false;
        }
    }
    

    Make a copy of Zend_Navigation that extends My_Navigation_Container:

    class My_Navigation extends My_Navigation_Container
    {
        /**
         * Creates a new navigation container
         *
         * @param array|Zend_Config $pages    [optional] pages to add
         * @throws Zend_Navigation_Exception  if $pages is invalid
         */
        public function __construct($pages = null)
        {
            if (is_array($pages) || $pages instanceof Zend_Config) {
                $this->addPages($pages);
            } elseif (null !== $pages) {
                throw new Zend_Navigation_Exception('Invalid argument: $pages must be an array, an instance of Zend_Config, or null');
            }
        }
    }