diff --git a/src/Knp/Menu/Renderer/BreadcrumbRenderer.php b/src/Knp/Menu/Renderer/BreadcrumbRenderer.php new file mode 100644 index 00000000..a7aab600 --- /dev/null +++ b/src/Knp/Menu/Renderer/BreadcrumbRenderer.php @@ -0,0 +1,160 @@ +defaultOptions = array_merge(array( + 'current_as_link' => true, + 'current_class' => 'current', + 'additional_path' => null, + 'compressed' => false, + 'allow_safe_labels' => false, + 'root_attributes' => array(), + ), $defaultOptions); + + parent::__construct($charset); + } + + /** + * Renders a menu with the specified renderer. + * + * @param \Knp\Menu\ItemInterface $item + * @param array $options + * @return string + */ + public function render(ItemInterface $item, array $options = array()) + { + $options = array_merge($this->defaultOptions, $options); + + $breadcrumb = $item->getBreadcrumbsArray($options['additional_path']); + + if (empty($breadcrumb)) { + return ''; + } + + return $this->renderBreadcrumb($breadcrumb, $options); + } + + /** + * Renders the breadcrumb + * + * @param array $breadcrumb + * @param array $options + * @return string + */ + protected function renderBreadcrumb(array $breadcrumb, array $options) + { + $html = $this->format('renderHtmlAttributes($options['root_attributes']).'>', 'ul', 0, $options); + $html .= $this->renderList($breadcrumb, $options); + $html .= $this->format('', 'ul', 0, $options); + + return $html; + } + + /** + * Renders the breadcrumb list + * + * @param array $breadcrumb + * @param array $options + * @return string + */ + protected function renderList(array $breadcrumb, array $options) + { + $html = ''; + foreach ($breadcrumb as $element) { + $element = array_replace(array('label' => null, 'uri' => null, 'item' => null), $element); + $html .= $this->renderItem($element['label'], $element['uri'], $options, $element['item']); + } + + return $html; + } + + /** + * @param string $label + * @param string $uri + * @param array $options + * @param \Knp\Menu\ItemInterface|null $item + * @return string + */ + protected function renderItem($label, $uri, array $options, ItemInterface $item = null) + { + $isCurrent = null !== $item && $item->isCurrent(); + $attributes = $isCurrent ? array('class' => $options['current_class']) : array(); + + // opening li tag + $html = $this->format('renderHtmlAttributes($attributes).'>', 'li', 1, $options); + + // render the text/link inside the li tag + if (null === $uri || ($isCurrent && !$options['current_as_link'])) { + $content = $this->renderLabel($label, $options, $item); + } else { + $content = sprintf('%s', $this->escape($uri), $this->renderLabel($label, $options, $item)); + } + $html .= $this->format($content, 'link', 1, $options); + + // closing li tag + $html .= $this->format('', 'li', 1, $options); + + return $html; + } + + /** + * @param string $label + * @param array $options + * @param \Knp\Menu\ItemInterface|null $item + * @return string + */ + protected function renderLabel($label, array $options, ItemInterface $item = null) + { + if ($options['allow_safe_labels'] && null !== $item && $item->getExtra('safe_label', false)) { + return $item->getLabel(); + } + + return $this->escape($label); + } + + /** + * If $this->renderCompressed is on, this will apply the necessary + * spacing and line-breaking so that the particular thing being rendered + * makes up its part in a fully-rendered and spaced menu. + * + * @param string $html The html to render in an (un)formatted way + * @param string $type The type [ul,link,li] of thing being rendered + * @param integer $level + * @param array $options + * @return string + */ + protected function format($html, $type, $level, array $options) + { + if ($options['compressed']) { + return $html; + } + + switch ($type) { + case 'ul': + case 'link': + $spacing = $level * 4; + break; + + case 'li': + $spacing = $level * 4 - 2; + break; + } + + return str_repeat(' ', $spacing).$html."\n"; + } +} diff --git a/tests/Knp/Menu/Tests/Renderer/BreadcrumbRendererTest.php b/tests/Knp/Menu/Tests/Renderer/BreadcrumbRendererTest.php new file mode 100644 index 00000000..f01b5f24 --- /dev/null +++ b/tests/Knp/Menu/Tests/Renderer/BreadcrumbRendererTest.php @@ -0,0 +1,115 @@ + true)); + + $expected = ''; + + $this->assertEquals($expected, $renderer->render($this->ch1)); + } + + public function testAdditionalPath() + { + $renderer = new BreadcrumbRenderer(array('compressed' => true)); + + $expected = ''; + + $this->assertEquals($expected, $renderer->render($this->ch1, array('additional_path' => array('Foo' => 'http://example.com')))); + } + + public function testCurrentLink() + { + $this->pt1->setUri('foobar')->setCurrent(true); + + $renderer = new BreadcrumbRenderer(array('compressed' => true)); + + $expected = ''; + + $this->assertEquals($expected, $renderer->render($this->ch1)); + } + + public function testCurrentNoLink() + { + $this->pt1->setUri('foobar')->setCurrent(true); + + $renderer = new BreadcrumbRenderer(array('compressed' => true, 'current_as_link' => false)); + + $expected = ''; + + $this->assertEquals($expected, $renderer->render($this->ch1)); + } + + public function testCurrentCustomClass() + { + $this->pt1->setCurrent(true); + + $renderer = new BreadcrumbRenderer(array('compressed' => true, 'current_class' => 'foo')); + + $expected = ''; + + $this->assertEquals($expected, $renderer->render($this->ch1)); + } + + public function testEscapedLabel() + { + $this->pt1->setExtra('safe_label', true)->setLabel('Foo'); + + $renderer = new BreadcrumbRenderer(array('compressed' => true)); + + $expected = ''; + + $this->assertEquals($expected, $renderer->render($this->ch1)); + } + + public function testUnsafeLabel() + { + $this->pt1->setLabel('Foo'); + + $renderer = new BreadcrumbRenderer(array('compressed' => true, 'allow_safe_labels' => true)); + + $expected = ''; + + $this->assertEquals($expected, $renderer->render($this->ch1)); + } + + public function testSafeLabel() + { + $this->pt1->setExtra('safe_label', true)->setLabel('Foo'); + + $renderer = new BreadcrumbRenderer(array('compressed' => true, 'allow_safe_labels' => true)); + + $expected = ''; + + $this->assertEquals($expected, $renderer->render($this->ch1)); + } + + public function testPrettyRendering() + { + $renderer = new BreadcrumbRenderer(); + + $rendered = << +
  • + Root li +
  • +
  • + Parent 1 +
  • +
  • + Child 1 +
  • + + +HTML; + + $this->assertEquals($rendered, $renderer->render($this->ch1)); + } +}