Skip to content

Commit

Permalink
Wrote Twig extension and event listener. Added addBreadcrumbs() and c…
Browse files Browse the repository at this point in the history
…aching for getBreadcrumbs() into BreadcrumbService. Added tests for routes with prefixes and for routes without requirements.
  • Loading branch information
Peter Hillerström committed Jul 2, 2012
1 parent 4f06606 commit da13663
Show file tree
Hide file tree
Showing 13 changed files with 299 additions and 40 deletions.
2 changes: 0 additions & 2 deletions Controller/DefaultController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@

use Symfony\Bundle\FrameworkBundle\Controller\Controller;


class DefaultController extends Controller
{

public function indexAction($name)
{
return $this->render('XiBreadcrumbsBundle:Default:index.html.twig', array('name' => $name));
Expand Down
21 changes: 15 additions & 6 deletions EventListener/BreadcrumbListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,33 @@
*
* @author Peter Hillerström <[email protected]>
*/
class BreadcrumbsListener extends Controller
class BreadcrumbListener extends Controller
{

/**
* @var ContainerInterface
*/
protected $container;

public function __construct(/* ContainerInterface */ $container)
public function __construct($container)
{
$this->container = $container;
$this->service = $this->container->get('xi_breadcrumbs');
$this->router = $this->container->get('router');
}

public function onKernelController(/* FilterControllerEvent */ $event)
public function onKernelController(FilterControllerEvent $event)
{
$logger = $this->container->get('logger');
$logger->debug('Got kernel.controller event');
if (!is_array($controller = $event->getController())) {
return;
}

/* $router = $this->container->get('router'); */
$controller = $event->getController();
$request = $event->getRequest();

$route = $request->get('_route');
$params = $this->router->match($request->getRequestUri());

$this->service->addBreadcrumbs($route, $params);
}
}
19 changes: 17 additions & 2 deletions Resources/config/routing.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
XiBreadcrumbsBundle_homepage:
hello:
pattern: /hello/{name}
defaults: { _controller: XiBreadcrumbsBundle:Default:index }
defaults:
_controller: XiBreadcrumbsBundle:Default:index
label: "hello {name}"
name: "stranger"

doing:
pattern: /hello/{name}/do/{thing}
defaults:
_controller: XiBreadcrumbsBundle:Default:index
parent: "hello"
label: "doing {thing}"
name: "stranger"
thing: "that"
requirements:
name: "\w+"
thing: "\w+"
10 changes: 9 additions & 1 deletion Resources/config/services.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
parameters:
xi_breadcrumbs.class: Xi\Bundle\BreadcrumbsBundle\Service\BreadcrumbService
xi_breadcrumbs_listener.class: Xi\Bundle\BreadcrumbsBundle\EventListener\BreadcrumbListener
xi_breadcrumbs_twig.class: Xi\Bundle\BreadcrumbsBundle\Twig\Extension\BreadcrumbExtension

services:
xi_breadcrumbs:
Expand All @@ -13,4 +14,11 @@ services:
arguments:
- "@service_container"
tags:
- { name: kernel.listener, event: onKernelController, method: onKernelController }
- { name: kernel.listener, event: onKernelController, method: onKernelController }

xi_breadcrumbs_twig:
class: %xi_breadcrumbs_twig.class%
arguments:
- "@service_container"
tags:
- { name: twig.extension }
11 changes: 11 additions & 0 deletions Resources/views/Default/breadcrumbs.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{% if breadcrumbs|length %}
<ul id="xi-breadcrumbs">
{% for bc in breadcrumbs %}
{% if loop.last or not bc.url %}
<li>{{ bc.label }}</li>
{% else %}
<li><a href="{{ bc.url }}">{{ bc.label }}</a></li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
10 changes: 9 additions & 1 deletion Resources/views/Default/index.html.twig
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
Hello {{ name }}!
<html>
<head>
<title>Hello {{ name }}!</title>
</head>
<body>
<p>{{ xi_breadcrumbs() }}</p>
<h1>Hello {{ name }}!</h1>
</body>
</html>
48 changes: 38 additions & 10 deletions Service/BreadcrumbService.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ class BreadcrumbService
*/
protected $router;

/**
* Used as a dict to save/cache Breadcrumbs for route and parameter combinations
* @var array
*/
private $cache = array();

public function __construct(ContainerInterface $container)
{
$this->container = $container;
Expand All @@ -52,8 +58,13 @@ public function setRouter(RouterInterface $router)
* @param string $name
* @return array
*/
public function getBreadcrumbs($name, array $params = array())
public function getBreadcrumbs($name, array $params = array(), $caching = false)
{
$hash = $this->getHash($name, $this->matchParams($name, $params));
if (!$caching && array_key_exists($hash, $this->cache)) {
return $this->cache[$hash];
}

$route = $this->getRoute($name);
$parents = $this->getParents($name);
$breadcrumbs = array();
Expand All @@ -69,9 +80,18 @@ public function getBreadcrumbs($name, array $params = array())
}
}

$this->cache[$hash] = $breadcrumbs;
return $breadcrumbs;
}

public function addBreadcrumbs($route, array $params = array())
{
$matched = $this->matchParams($route, $params);
if ($bc = $this->getBreadcrumbs($route, $matched, true)) {
$this->cache[$this->getHash($route, $matched)] = $bc;
}
}

/**
* @param string $route
* @return array
Expand All @@ -97,7 +117,8 @@ public function getParents($route)
private function getParent($name) {
$route = $this->getRoute($name);
if ($route && $route->hasDefault('parent')) {
return $route->getDefault('parent');
$parent = $route->getDefault('parent');
return ($this->getRoute($parent) ? $parent : null);
} else {
return null;
}
Expand Down Expand Up @@ -162,7 +183,14 @@ private function matchParams($name, array $params, $fromLabel = false)
return array();
}

$reqs = $route->getRequirements();
// Ensure we have requirements
if (!$reqs = $route->getRequirements()) {
$template = $fromLabel
? $route->getDefault('label')
: $route->getPattern();
preg_match_all(self::TWIG_TAG, $template, $matches);
$reqs = array_flip($matches[1]);
}

// Get default values for missing parameters
foreach ($route->getDefaults() as $def => $value) {
Expand All @@ -171,18 +199,14 @@ private function matchParams($name, array $params, $fromLabel = false)
}
}

// Return matched params
if (!empty($params) && $reqs) {
return array_intersect_key($params, $reqs);
} else {
$template = $fromLabel
? $route->getDefault('label')
: $route->getPattern();
return preg_split(self::TWIG_TAG, $template);
}

} else {
return array();
}

return array();
}

/**
Expand All @@ -196,4 +220,8 @@ private function getRoute($name)
}
return $this->getRouter()->getRouteCollection()->get($name);
}

private function getHash($route, $params) {
return hash('sha1', json_encode(array_merge($params, array('route' => $route))));
}
}
80 changes: 63 additions & 17 deletions Tests/EventListener/BreadcrumbListenerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,93 @@

namespace Xi\Bundle\BreadcrumbsBundle\Tests\Service;

use \Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use \Symfony\Component\EventDispatcher\EventDispatcher;
use \Symfony\Component\EventDispatcher\Event;
use \Symfony\Component\DomCrawler\Crawler;
use \Xi\Bundle\BreadcrumbsBundle\Service\BreadcrumbsService;
use \Xi\Bundle\BreadcrumbsBundle\EventListener\BreadcrumbsListener;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;

use Xi\Bundle\BreadcrumbsBundle\Controller\DefaultController;
use Xi\Bundle\BreadcrumbsBundle\EventListener\BreadcrumbListener;
use Xi\Bundle\BreadcrumbsBundle\Model\Breadcrumb;
use Xi\Bundle\BreadcrumbsBundle\Service\BreadcrumbService;

/**
* @group events
*/
class BreadcrumbsServiceEventTest extends WebTestCase
class BreadcrumbServiceEventTest extends WebTestCase
{
/**
* @var BreadcrumbsService
* @var BreadcrumbService
*/
protected $service;

public function setUp()
static protected $kernel;

protected function setUp()
{
parent::setUp();

$this->client = static::createClient();
$this->container = $this->client->getContainer();
$this->service = new BreadcrumbsService($this->container);
$this->listener = new BreadcrumbsListener($this->container);

$this->dispatcher = new EventDispatcher();
$this->listener = new BreadcrumbListener($this->container);
$this->service = new BreadcrumbService($this->container);

static::$kernel = static::createKernel();

$this->resolver = new ControllerResolver(
$this->container,
new ControllerNameParser(static::$kernel)
);

}

protected function tearDown()
{
parent::tearDown();

$this->dispatcher = null;
$this->listener = null;
$this->service = null;
}

/**
* @test
* @group events
*/
public function testOnKernelControllerEvent() {
$dispatcher = new EventDispatcher();
$dispatcher->addListener('kernel.controller', array($this->listener, 'onKernelController'));
public function testOnKernelControllerEvent()
{
$this->dispatcher->addListener('kernel.controller', array($this->listener, 'onKernelController'));

$crawler = $this->client->request('GET', '/hello/Peter/do/play');
$request = $this->client->getRequest();
$controller = $this->resolver->getController($request);

$fcEvent = new FilterControllerEvent(
$this->getKernel(),
$controller,
$request,
HttpKernelInterface::MASTER_REQUEST
);

//$crawler = $this->client->request('GET', '/hello/Peter');
$this->dispatcher->dispatch('kernel.controller', $fcEvent);

$dispatcher->dispatch('kernel.controller',
new Event($this->listener, 'xyz')
$bc = array(
'hello' => new Breadcrumb('hello Peter', '/hello/Peter'),
'doing' => new Breadcrumb('doing play', '/hello/Peter/do/play')
);

$this->assertEquals($bc, $this->service->getBreadcrumbs('doing', array(
'name' => 'Peter',
'thing' => 'play'
)));
}

private function getKernel()
{
return static::$kernel;
}
}
18 changes: 18 additions & 0 deletions Tests/Fixtures/no_requirements.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
hello:
pattern: /hello/{name}
defaults:
_controller: XiBreadcrumbsBundle:Default:index
label: "hello {name}"
name: "stranger"

doing:
pattern: /hello/{name}/do/{thing}
defaults:
_controller: XiBreadcrumbsBundle:Default:index
parent: "hello"
label: "doing {thing}"
name: "stranger"
thing: "that"
requirements:
name: "\w+"
thing: "\w+"
6 changes: 6 additions & 0 deletions Tests/Fixtures/prefix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
home:
pattern: /home

prefix:
resource: "simple.yml"
prefix: /prefix
3 changes: 2 additions & 1 deletion Tests/Fixtures/simple.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
root:
pattern: /
defaults: { _controller: XiBreadcrumbsBundle:Default:index }
defaults:
parent: "home" # See prefix.yml

foo:
pattern: /foo
Expand Down
Loading

0 comments on commit da13663

Please sign in to comment.