url = $url; } else { $this->url = Url::fromPath($url); } $this->defaultRenderer = new MenuItemRenderer(); parent::__construct(new PermittedMenuItemFilter($menu), RecursiveIteratorIterator::CHILD_FIRST); } /** * @param bool $value */ public function useCustomRenderer($value = true) { $this->useCustomRenderer = $value; return $this; } /** * Register the outer ul opening html-tag */ public function beginIteration() { $this->tags[] = ''; } /** * Register a inner ul opening html-tag */ public function beginChildren() { // The iterator runs in mode CHILD_FIRST so we need to remember // where to insert the parent's opening html tag once its rendered $parent = $this->getSubIterator(0)->current(); $this->tags[$parent->getId() . '_begin'] = null; $this->tags[] = ''; // Remember the position of the parent's closing html-tag $parent = $this->getSubIterator(0)->current(); $this->tags[$parent->getId() . '_end'] = null; } /** * Render the given child * * @param Menu $child The menu's child to render * * @return string The child rendered as html */ public function renderChild(Menu $child) { if ($child->getRenderer() !== null && $this->useCustomRenderer) { try { return $child->getRenderer()->render($child); } catch (Exception $e) { Logger::error( 'Could not invoke custom menu renderer. %s in %s:%d with message: %s', get_class($e), $e->getFile(), $e->getLine(), $e->getMessage() ); } } return $this->defaultRenderer->render($child); } /** * Return the menu rendered as html * * @return string */ public function render() { $passedActiveChild = false; foreach ($this as $child) { $childIsActive = $this->isActive($child); if ($childIsActive && $this->getDepth() > 0) { $passedActiveChild = true; } if ($childIsActive || ($passedActiveChild && $this->getDepth() === 0)) { $passedActiveChild &= $this->getDepth() !== 0; $openTag = '
  • '; } else { $openTag = '
  • '; } $content = $this->renderChild($child); $closingTag = '
  • '; if (array_key_exists($child->getId() . '_begin', $this->tags)) { $this->tags[$child->getId() . '_begin'] = $openTag . $content; $this->tags[$child->getId() . '_end'] = $closingTag; } else { $this->tags[] = $openTag . $content . $closingTag; } } return implode("\n", $this->tags); } /** * @see MenuRenderer::render() */ public function __toString() { return $this->render(); } /** * Return whether the current request url references the child's url * * @param Menu $child The menu's child to check * * @return bool */ protected function isActive(Menu $child) { if (! $this->url) return false; if (! ($childUrl = $child->getUrl())) return false; return $this->url && $this->url->matches($childUrl); } }