diff --git a/src/Knp/Menu/Renderer/BaseRenderer.php b/src/Knp/Menu/Renderer/BaseRenderer.php
new file mode 100644
index 00000000..96a0d7aa
--- /dev/null
+++ b/src/Knp/Menu/Renderer/BaseRenderer.php
@@ -0,0 +1,30 @@
+defaultOptions = array_merge(array(
+ 'depth' => null,
+ 'matchingDepth' => null,
+ 'currentAsLink' => true,
+ 'currentClass' => 'current',
+ 'ancestorClass' => 'current_ancestor',
+ 'firstClass' => 'first',
+ 'lastClass' => 'last',
+ 'compressed' => false,
+ 'allow_safe_labels' => false,
+ 'clear_matcher' => true,
+ 'leaf_class' => null,
+ 'branch_class' => null,
+ ), $defaultOptions);
+ }
+
+ public abstract function render(ItemInterface $item, array $options = array());
+}
diff --git a/src/Knp/Menu/Renderer/BreadcrumbRenderer.php b/src/Knp/Menu/Renderer/BreadcrumbRenderer.php
new file mode 100644
index 00000000..4a87b3e6
--- /dev/null
+++ b/src/Knp/Menu/Renderer/BreadcrumbRenderer.php
@@ -0,0 +1,159 @@
+itemManipulator = $itemManipulator;
+ $defaultOptions = array_merge(array(
+ 'additional_path' => null,
+ 'root_attributes' => array(),
+ ), $defaultOptions);
+
+ parent::__construct($defaultOptions, $charset);
+ }
+
+ /**
+ * Renders a menu with the specified renderer.
+ *
+ * @param ItemInterface $item
+ * @param array $options
+ * @return string
+ */
+ public function render(ItemInterface $item, array $options = array())
+ {
+ $options = array_merge($this->defaultOptions, $options);
+
+ $breadcrumb = $this->itemManipulator->getBreadcrumbsArray($item, $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 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['currentClass']) : 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['currentAsLink'])) {
+ $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 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/src/Knp/Menu/Renderer/ListRenderer.php b/src/Knp/Menu/Renderer/ListRenderer.php
index 4d7db5d0..5e355b6b 100644
--- a/src/Knp/Menu/Renderer/ListRenderer.php
+++ b/src/Knp/Menu/Renderer/ListRenderer.php
@@ -8,10 +8,9 @@
/**
* Renders MenuItem tree as unordered list
*/
-class ListRenderer extends Renderer implements RendererInterface
+class ListRenderer extends Renderer
{
protected $matcher;
- protected $defaultOptions;
/**
* @param MatcherInterface $matcher
@@ -21,22 +20,8 @@ class ListRenderer extends Renderer implements RendererInterface
public function __construct(MatcherInterface $matcher, array $defaultOptions = array(), $charset = null)
{
$this->matcher = $matcher;
- $this->defaultOptions = array_merge(array(
- 'depth' => null,
- 'matchingDepth' => null,
- 'currentAsLink' => true,
- 'currentClass' => 'current',
- 'ancestorClass' => 'current_ancestor',
- 'firstClass' => 'first',
- 'lastClass' => 'last',
- 'compressed' => false,
- 'allow_safe_labels' => false,
- 'clear_matcher' => true,
- 'leaf_class' => null,
- 'branch_class' => null,
- ), $defaultOptions);
-
- parent::__construct($charset);
+
+ parent::__construct($defaultOptions, $charset);
}
public function render(ItemInterface $item, array $options = array())
diff --git a/src/Knp/Menu/Renderer/Renderer.php b/src/Knp/Menu/Renderer/Renderer.php
index a82de569..c30daa2e 100644
--- a/src/Knp/Menu/Renderer/Renderer.php
+++ b/src/Knp/Menu/Renderer/Renderer.php
@@ -6,18 +6,20 @@
define('ENT_SUBSTITUTE', 8);
}
-abstract class Renderer
+abstract class Renderer extends BaseRenderer
{
protected $charset = 'UTF-8';
/**
* @param string $charset
*/
- public function __construct($charset = null)
+ public function __construct($defaultOptions = array(), $charset = null)
{
if (null !== $charset) {
$this->charset = (string) $charset;
}
+
+ parent::__construct($defaultOptions);
}
/**
diff --git a/src/Knp/Menu/Renderer/TwigRenderer.php b/src/Knp/Menu/Renderer/TwigRenderer.php
index 4d379f05..60b7a03f 100644
--- a/src/Knp/Menu/Renderer/TwigRenderer.php
+++ b/src/Knp/Menu/Renderer/TwigRenderer.php
@@ -5,14 +5,13 @@
use Knp\Menu\ItemInterface;
use Knp\Menu\Matcher\MatcherInterface;
-class TwigRenderer implements RendererInterface
+class TwigRenderer extends BaseRenderer
{
/**
* @var \Twig_Environment
*/
private $environment;
private $matcher;
- private $defaultOptions;
/**
* @param \Twig_Environment $environment
@@ -24,21 +23,12 @@ public function __construct(\Twig_Environment $environment, $template, MatcherIn
{
$this->environment = $environment;
$this->matcher = $matcher;
- $this->defaultOptions = array_merge(array(
- 'depth' => null,
- 'matchingDepth' => null,
- 'currentAsLink' => true,
- 'currentClass' => 'current',
- 'ancestorClass' => 'current_ancestor',
- 'firstClass' => 'first',
- 'lastClass' => 'last',
- 'template' => $template,
- 'compressed' => false,
- 'allow_safe_labels' => false,
- 'clear_matcher' => true,
- 'leaf_class' => null,
- 'branch_class' => null,
+
+ $defaultOptions = array_merge(array(
+ 'template' => $template
), $defaultOptions);
+
+ parent::__construct($defaultOptions);
}
public function render(ItemInterface $item, array $options = array())
diff --git a/tests/Knp/Menu/Tests/Renderer/BreadcrumbRendererTest.php b/tests/Knp/Menu/Tests/Renderer/BreadcrumbRendererTest.php
new file mode 100644
index 00000000..8d75b2cf
--- /dev/null
+++ b/tests/Knp/Menu/Tests/Renderer/BreadcrumbRendererTest.php
@@ -0,0 +1,116 @@
+ true));
+
+ $expected = '';
+
+ $this->assertEquals($expected, $renderer->render($this->ch1));
+ }
+
+ public function testAdditionalPath()
+ {
+ $renderer = new BreadcrumbRenderer(new MenuManipulator, array('compressed' => true));
+
+ $expected = '- Root li
- Parent 1
- Child 1
- Foo
';
+
+ $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(new MenuManipulator, array('compressed' => true));
+
+ $expected = '';
+
+ $this->assertEquals($expected, $renderer->render($this->ch1));
+ }
+
+ public function testCurrentNoLink()
+ {
+ $this->pt1->setUri('foobar')->setCurrent(true);
+
+ $renderer = new BreadcrumbRenderer(new MenuManipulator, array('compressed' => true, 'currentAsLink' => false));
+
+ $expected = '';
+
+ $this->assertEquals($expected, $renderer->render($this->ch1));
+ }
+
+ public function testCurrentCustomClass()
+ {
+ $this->pt1->setCurrent(true);
+
+ $renderer = new BreadcrumbRenderer(new MenuManipulator, array('compressed' => true, 'currentClass' => 'foo'));
+
+ $expected = '';
+
+ $this->assertEquals($expected, $renderer->render($this->ch1));
+ }
+
+ public function testEscapedLabel()
+ {
+ $this->pt1->setExtra('safe_label', true)->setLabel('Foo');
+
+ $renderer = new BreadcrumbRenderer(new MenuManipulator, array('compressed' => true));
+
+ $expected = '- Root li
- <strong>Foo</strong>
- Child 1
';
+
+ $this->assertEquals($expected, $renderer->render($this->ch1));
+ }
+
+ public function testUnsafeLabel()
+ {
+ $this->pt1->setLabel('Foo');
+
+ $renderer = new BreadcrumbRenderer(new MenuManipulator, array('compressed' => true, 'allow_safe_labels' => true));
+
+ $expected = '- Root li
- <strong>Foo</strong>
- Child 1
';
+
+ $this->assertEquals($expected, $renderer->render($this->ch1));
+ }
+
+ public function testSafeLabel()
+ {
+ $this->pt1->setExtra('safe_label', true)->setLabel('Foo');
+
+ $renderer = new BreadcrumbRenderer(new MenuManipulator, array('compressed' => true, 'allow_safe_labels' => true));
+
+ $expected = '';
+
+ $this->assertEquals($expected, $renderer->render($this->ch1));
+ }
+
+ public function testPrettyRendering()
+ {
+ $renderer = new BreadcrumbRenderer(new MenuManipulator);
+
+ $rendered = <<
+
+ Root li
+
+
+ Parent 1
+
+
+ Child 1
+
+
+
+HTML;
+
+ $this->assertEquals($rendered, $renderer->render($this->ch1));
+ }
+}