Skip to content

Commit

Permalink
Introduced factory extension to provide better hooks in the factory
Browse files Browse the repository at this point in the history
  • Loading branch information
stof committed Jun 23, 2013
1 parent e02f7fa commit 2505642
Show file tree
Hide file tree
Showing 11 changed files with 323 additions and 105 deletions.
10 changes: 8 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## 2.0.0 (2013-XX-XX)
## 2.0.0 beta 1 (2013-XX-XX)

* Introduced extension points in the MenuFactory through `Knp\Menu\Factory\ExtensionInterface`
* [BC break compared to 2.0 alpha 1] The inheritance extension points introduced in alpha1 are
deprecated in favor of extensions and will be removed before the stable release.
* `Knp\Menu\Silex\RouterAwareFactory` is deprecated in favor of `Knp\Menu\Silex\RoutingExtension`.
* [BC break] Deprecated the methods `createFromArray` and `createFromNode` in the MenuFactory and
removed them from `Knp\Menu\FactoryInterface`. Use `Knp\Menu\Loader\ArrayLoader` and
`Knp\Menu\Loader\NodeLoader` instead.
Expand All @@ -9,7 +13,7 @@
instead.
* Made the RouterVoter comaptible with SensioFrameworkExtraBundle param converters
* Added the possibility to match routes using a regex on their name in the RouterVoter
* [BC break] Refactored the RouterVoter to make it more flexible
* [BC break compared to 2.0 alpha 1] Refactored the RouterVoter to make it more flexible
The way to pass routes in the item extras has changed.

Before:
Expand All @@ -36,10 +40,12 @@

## 2.0.0 alpha 1 (2013-06-23)

* Added protected methods `buildOptions` and `configureItem` in the MenuFactory as extension point by inheritance
* [BC break] Refactored the way to mark items as current
``setCurrentUri``, ``getCurrentUri`` and ``getCurrentItem`` have been removed from the ItemInterface.
Determining the current items is now delegated to a matcher, and the default implementation
uses voters to apply the matching. Getting the current items can be done thanks to the CurrentItemFilterIterator.
* [BC break] The signature of the CurrentItemFilterIterator constructor changed to accept the item matcher
* [BC break] Changed the format of the breadcrumb array
Instead of storing the elements with the label as key and the uri as value
the array now stores an array of array elements with 3 keys: `label`, `uri` and `item`.
Expand Down
59 changes: 59 additions & 0 deletions src/Knp/Menu/Factory/CoreExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace Knp\Menu\Factory;

use Knp\Menu\ItemInterface;

/**
* core factory extension with the main logic
*/
class CoreExtension implements ExtensionInterface
{
/**
* Builds the full option array used to configure the item.
*
* @param array $options
*
* @return array
*/
public function buildOptions(array $options)
{
return array_merge(
array(
'uri' => null,
'label' => null,
'attributes' => array(),
'linkAttributes' => array(),
'childrenAttributes' => array(),
'labelAttributes' => array(),
'extras' => array(),
'current' => null,
'display' => true,
'displayChildren' => true,
),
$options
);
}

/**
* Configures the newly created item with the passed options
*
* @param ItemInterface $item
* @param array $options
*/
public function buildItem(ItemInterface $item, array $options)
{
$item
->setUri($options['uri'])
->setLabel($options['label'])
->setAttributes($options['attributes'])
->setLinkAttributes($options['linkAttributes'])
->setChildrenAttributes($options['childrenAttributes'])
->setLabelAttributes($options['labelAttributes'])
->setExtras($options['extras'])
->setCurrent($options['current'])
->setDisplay($options['display'])
->setDisplayChildren($options['displayChildren'])
;
}
}
25 changes: 25 additions & 0 deletions src/Knp/Menu/Factory/ExtensionInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Knp\Menu\Factory;

use Knp\Menu\ItemInterface;

interface ExtensionInterface
{
/**
* Builds the full option array used to configure the item.
*
* @param array $options The options processed by the previous extensions
*
* @return array
*/
public function buildOptions(array $options);

/**
* Configures the item with the passed options
*
* @param ItemInterface $item
* @param array $options
*/
public function buildItem(ItemInterface $item, array $options);
}
85 changes: 49 additions & 36 deletions src/Knp/Menu/MenuFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Knp\Menu;

use Knp\Menu\Factory\CoreExtension;
use Knp\Menu\Factory\ExtensionInterface;
use Knp\Menu\Loader\ArrayLoader;
use Knp\Menu\Loader\NodeLoader;

Expand All @@ -10,60 +12,71 @@
*/
class MenuFactory implements FactoryInterface
{
/**
* @var \SplPriorityQueue|ExtensionInterface[]
*/
private $extensions;

public function __construct()
{
$this->extensions = new \SplPriorityQueue();

$this->addExtension(new CoreExtension(), -10);
}

public function createItem($name, array $options = array())
{
// TODO remove this BC layer before releasing 2.0
$processedOptions = $this->buildOptions($options);
if ($processedOptions !== $options) {
trigger_error(sprintf('Overwriting Knp\Menu\MenuFactory::buildOptions is deprecated. Use a factory extension instead of %s.', get_class($this)), E_USER_DEPRECATED);

$options = $processedOptions;
}

foreach (clone $this->extensions as $extension) {
$options = $extension->buildOptions($options);
}

$item = new MenuItem($name, $this);

$options = $this->buildOptions($options);
$this->configureItem($item, $options);
foreach (clone $this->extensions as $extension) {
$extension->buildItem($item, $options);
}

// TODO remove this BC layer before releasing 2.0
if (method_exists($this, 'configureItem')) {
trigger_error(sprintf('Overwriting Knp\Menu\MenuFactory::configureItem is deprecated. Use a factory extension instead of %s.', get_class($this)), E_USER_DEPRECATED);

$this->configureItem($item, $options);
}

return $item;
}

/**
* Builds the full option array used to configure the item.
*
* @param array $options
* Adds a factory extension
*
* @return array
* @param ExtensionInterface $extension
* @param integer $priority
*/
protected function buildOptions(array $options)
public function addExtension(ExtensionInterface $extension, $priority = 0)
{
return array_merge(
array(
'uri' => null,
'label' => null,
'attributes' => array(),
'linkAttributes' => array(),
'childrenAttributes' => array(),
'labelAttributes' => array(),
'extras' => array(),
'display' => true,
'displayChildren' => true,
),
$options
);
$this->extensions->insert($extension, $priority);
}

/**
* Configures the newly created item with the passed options
* Builds the full option array used to configure the item.
*
* @param ItemInterface $item
* @param array $options
* @deprecated Use a Knp\Menu\Factory\ExtensionInterface instead
*
* @param array $options
*
* @return array
*/
protected function configureItem(ItemInterface $item, array $options)
protected function buildOptions(array $options)
{
$item
->setUri($options['uri'])
->setLabel($options['label'])
->setAttributes($options['attributes'])
->setLinkAttributes($options['linkAttributes'])
->setChildrenAttributes($options['childrenAttributes'])
->setLabelAttributes($options['labelAttributes'])
->setExtras($options['extras'])
->setDisplay($options['display'])
->setDisplayChildren($options['displayChildren'])
;
return $options;
}

/**
Expand Down
6 changes: 4 additions & 2 deletions src/Knp/Menu/Silex/KnpMenuServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ class KnpMenuServiceProvider implements ServiceProviderInterface
public function register(Application $app)
{
$app['knp_menu.factory'] = $app->share(function () use ($app) {
$factory = new MenuFactory();

if (isset($app['url_generator'])) {
return new RouterAwareFactory($app['url_generator']);
$factory->addExtension(new RoutingExtension($app['url_generator']));
}

return new MenuFactory();
return $factory;
});

$app['knp_menu.matcher'] = $app->share(function () use ($app) {
Expand Down
24 changes: 5 additions & 19 deletions src/Knp/Menu/Silex/RouterAwareFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,16 @@

/**
* Factory able to use the Symfony2 Routing component to build the url
*
* @deprecated Use Knp\Menu\Silex\RoutingExtension instead
*/
class RouterAwareFactory extends MenuFactory
{
protected $generator;

public function __construct(UrlGeneratorInterface $generator)
{
$this->generator = $generator;
}

protected function buildOptions(array $options = array())
{
if (!empty($options['route'])) {
$params = isset($options['routeParameters']) ? $options['routeParameters'] : array();
$absolute = isset($options['routeAbsolute']) ? $options['routeAbsolute'] : false;
$options['uri'] = $this->generator->generate($options['route'], $params, $absolute);

// adding the item route to the extras under the 'routes' key (for the Silex RouteVoter)
$options['extras']['routes'][] = array(
'route' => $options['route'],
'parameters' => $params,
);
}
trigger_error(__CLASS__ . ' is deprecated. Use Knp\Menu\Silex\RoutingExtension instead.', E_USER_DEPRECATED);

return parent::buildOptions($options);
parent::__construct();
$this->addExtension(new RoutingExtension($generator));
}
}
41 changes: 41 additions & 0 deletions src/Knp/Menu/Silex/RoutingExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Knp\Menu\Silex;

use Knp\Menu\Factory\ExtensionInterface;
use Knp\Menu\ItemInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

/**
* Factory able to use the Symfony2 Routing component to build the url
*/
class RoutingExtension implements ExtensionInterface
{
private $generator;

public function __construct(UrlGeneratorInterface $generator)
{
$this->generator = $generator;
}

public function buildOptions(array $options = array())
{
if (!empty($options['route'])) {
$params = isset($options['routeParameters']) ? $options['routeParameters'] : array();
$absolute = isset($options['routeAbsolute']) ? $options['routeAbsolute'] : false;
$options['uri'] = $this->generator->generate($options['route'], $params, $absolute);

// adding the item route to the extras under the 'routes' key (for the Silex RouteVoter)
$options['extras']['routes'][] = array(
'route' => $options['route'],
'parameters' => $params,
);
}

return $options;
}

public function buildItem(ItemInterface $item, array $options)
{
}
}
30 changes: 30 additions & 0 deletions tests/Knp/Menu/Tests/MenuFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,36 @@

class MenuFactoryTest extends \PHPUnit_Framework_TestCase
{
public function testExtensions()
{
$factory = new MenuFactory();

$extension1 = $this->getMock('Knp\Menu\Factory\ExtensionInterface');
$extension1->expects($this->once())
->method('buildOptions')
->with(array('foo' => 'bar'))
->will($this->returnValue(array('uri' => 'foobar')));
$extension1->expects($this->once())
->method('buildItem')
->with($this->isInstanceOf('Knp\Menu\ItemInterface'), $this->contains('foobar'));

$factory->addExtension($extension1);

$extension2 = $this->getMock('Knp\Menu\Factory\ExtensionInterface');
$extension2->expects($this->once())
->method('buildOptions')
->with(array('foo' => 'baz'))
->will($this->returnValue(array('foo' => 'bar')));
$extension1->expects($this->once())
->method('buildItem')
->with($this->isInstanceOf('Knp\Menu\ItemInterface'), $this->contains('foobar'));

$factory->addExtension($extension2, 10);

$item = $factory->createItem('test', array('foo' => 'baz'));
$this->assertEquals('foobar', $item->getUri());
}

public function testCreateItem()
{
$factory = new MenuFactory();
Expand Down
9 changes: 0 additions & 9 deletions tests/Knp/Menu/Tests/Silex/KnpMenuServiceProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,6 @@ public function testFactoryWithoutRouter()
$this->assertEquals('Knp\Menu\MenuFactory', get_class($app['knp_menu.factory']));
}

public function testFactoryWithRouter()
{
$app = new Application();
$app->register(new KnpMenuServiceProvider());
$app->register(new UrlGeneratorServiceProvider());

$this->assertInstanceOf('Knp\Menu\Silex\RouterAwareFactory', $app['knp_menu.factory']);
}

public function testTwigRendererNotRegistered()
{
$app = new Application();
Expand Down
Loading

0 comments on commit 2505642

Please sign in to comment.