Router stores path variables directly as attributes by default.

This commit is contained in:
PJ Dietz 2015-05-19 21:06:50 -04:00
parent 0387255676
commit dedec4ec4e
2 changed files with 119 additions and 72 deletions

View File

@ -13,7 +13,7 @@ use WellRESTed\Routing\Route\RouteInterface;
class Router implements RouterInterface class Router implements RouterInterface
{ {
/** @var string Key to ServerRequestInterface attribute for matched path variables */ /** @var string Key to ServerRequestInterface attribute for matched path variables */
public $pathVariablesAttributeKey = "pathVariables"; private $pathVariablesAttributeName;
/** @var DispatcherInterface */ /** @var DispatcherInterface */
private $dispatcher; private $dispatcher;
/** @var RouteFactoryInterface */ /** @var RouteFactoryInterface */
@ -27,12 +27,25 @@ class Router implements RouterInterface
/** @var RouteInterface[] Hash array mapping path prefixes to routes */ /** @var RouteInterface[] Hash array mapping path prefixes to routes */
private $patternRoutes; private $patternRoutes;
public function __construct(DispatcherInterface $dispatcher = null) /**
* Create a new Router.
*
* When the router matches a route with path variables, it will add each
* variable as an attribute on the ServerRequestInterface by default.
*
* When $pathVariablesAttributeName is set, the router will set one
* attribute with the passed name to an array containing all of the path
* variables.
*
* @param DispatcherInterface $dispatcher Instance to use for dispatching
* middleware.
* @param string $pathVariablesAttributeName Optionally provide all path
* variables as an array stored with this attribute name
*/
public function __construct(DispatcherInterface $dispatcher = null, $pathVariablesAttributeName = null)
{ {
if ($dispatcher === null) {
$dispatcher = new Dispatcher();
}
$this->dispatcher = $dispatcher; $this->dispatcher = $dispatcher;
$this->pathVariablesAttributeName = $pathVariablesAttributeName;
$this->factory = $this->getRouteFactory($this->dispatcher); $this->factory = $this->getRouteFactory($this->dispatcher);
$this->routes = []; $this->routes = [];
$this->staticRoutes = []; $this->staticRoutes = [];
@ -40,6 +53,41 @@ class Router implements RouterInterface
$this->patternRoutes = []; $this->patternRoutes = [];
} }
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
{
// Use only the path for routing.
$requestTarget = parse_url($request->getRequestTarget(), PHP_URL_PATH);
$route = $this->getStaticRoute($requestTarget);
if ($route) {
return $route($request, $response, $next);
}
$route = $this->getPrefixRoute($requestTarget);
if ($route) {
return $route($request, $response, $next);
}
// Try each of the routes.
foreach ($this->patternRoutes as $route) {
if ($route->matchesRequestTarget($requestTarget)) {
$pathVariables = $route->getPathVariables();
if ($this->pathVariablesAttributeName) {
$request = $request->withAttribute($this->pathVariablesAttributeName, $pathVariables);
} else {
foreach ($pathVariables as $name => $value) {
$request = $request->withAttribute($name, $value);
}
}
return $route($request, $response, $next);
}
}
// If no route exists, set the status code of the response to 404 and
// return the response without propagating.
return $response->withStatus(404);
}
/** /**
* Register middleware with the router for a given path and method. * Register middleware with the router for a given path and method.
* *
@ -75,35 +123,6 @@ class Router implements RouterInterface
return $this; return $this;
} }
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
{
// Use only the path for routing.
$requestTarget = parse_url($request->getRequestTarget(), PHP_URL_PATH);
$route = $this->getStaticRoute($requestTarget);
if ($route) {
return $route($request, $response, $next);
}
$route = $this->getPrefixRoute($requestTarget);
if ($route) {
return $route($request, $response, $next);
}
// Try each of the routes.
foreach ($this->patternRoutes as $route) {
if ($route->matchesRequestTarget($requestTarget)) {
$pathVariables = $route->getPathVariables();
$request = $request->withAttribute($this->pathVariablesAttributeKey, $pathVariables);
return $route($request, $response, $next);
}
}
// If no route exists, set the status code of the response to 404 and
// return the response without propagating.
return $response->withStatus(404);
}
/** /**
* @param DispatcherInterface * @param DispatcherInterface
* @return RouteFactoryInterface * @return RouteFactoryInterface

View File

@ -75,20 +75,8 @@ class RouterTest extends \PHPUnit_Framework_TestCase
*/ */
public function testCreatesInstance() public function testCreatesInstance()
{ {
$routeMap = new Router($this->dispatcher->reveal()); $router = new Router($this->dispatcher->reveal());
$this->assertNotNull($routeMap); $this->assertNotNull($router);
}
/**
* @covers ::__construct
* @covers ::getRouteFactory
* @uses WellRESTed\Routing\Route\RouteFactory
* @uses WellRESTed\Dispatching\Dispatcher
*/
public function testCreatesInstanceWithDispatcherByDefault()
{
$routeMap = new Router();
$this->assertNotNull($routeMap);
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -339,30 +327,6 @@ class RouterTest extends \PHPUnit_Framework_TestCase
$patternRoute2->matchesRequestTarget(Argument::any())->shouldNotHaveBeenCalled(); $patternRoute2->matchesRequestTarget(Argument::any())->shouldNotHaveBeenCalled();
} }
/**
* @covers ::__invoke
* @group current
*/
public function testSetPathVariablesAttributeBeforeDispatchingPatternRoute()
{
$target = "/";
$variables = [
"id" => "1024",
"name" => "Molly"
];
$this->request->getRequestTarget()->willReturn($target);
$this->route->getTarget()->willReturn($target);
$this->route->getType()->willReturn(RouteInterface::TYPE_PATTERN);
$this->route->matchesRequestTarget(Argument::cetera())->willReturn(true);
$this->route->getPathVariables()->willReturn($variables);
$this->router->register("GET", $target, "middleware");
$this->router->__invoke($this->request->reveal(), $this->response->reveal(), $this->next);
$this->request->withAttribute("pathVariables", $variables)->shouldHaveBeenCalled();
}
/** /**
* @covers ::__invoke * @covers ::__invoke
* @covers ::registerRouteForTarget * @covers ::registerRouteForTarget
@ -382,6 +346,70 @@ class RouterTest extends \PHPUnit_Framework_TestCase
$this->route->matchesRequestTarget("/my/path")->shouldHaveBeenCalled(); $this->route->matchesRequestTarget("/my/path")->shouldHaveBeenCalled();
} }
// ------------------------------------------------------------------------
// Path Variables
/**
* @covers ::__invoke
* @dataProvider pathVariableProvider
*/
public function testSetPathVariablesAttributeIndividually($name, $value)
{
$attributeName = "pathVariables";
$target = "/";
$variables = [
"id" => "1024",
"name" => "Molly"
];
$this->request->getRequestTarget()->willReturn($target);
$this->route->getTarget()->willReturn($target);
$this->route->getType()->willReturn(RouteInterface::TYPE_PATTERN);
$this->route->matchesRequestTarget(Argument::cetera())->willReturn(true);
$this->route->getPathVariables()->willReturn($variables);
$this->router->__construct($this->dispatcher->reveal());
$this->router->register("GET", $target, "middleware");
$this->router->__invoke($this->request->reveal(), $this->response->reveal(), $this->next);
$this->request->withAttribute($name, $value)->shouldHaveBeenCalled();
}
public function pathVariableProvider()
{
return [
["id", "1024"],
["name", "Molly"]
];
}
/**
* @covers ::__invoke
*/
public function testSetPathVariablesAttributeAsArray()
{
$attributeName = "pathVariables";
$target = "/";
$variables = [
"id" => "1024",
"name" => "Molly"
];
$this->request->getRequestTarget()->willReturn($target);
$this->route->getTarget()->willReturn($target);
$this->route->getType()->willReturn(RouteInterface::TYPE_PATTERN);
$this->route->matchesRequestTarget(Argument::cetera())->willReturn(true);
$this->route->getPathVariables()->willReturn($variables);
$this->router->__construct($this->dispatcher->reveal(), $attributeName);
$this->router->register("GET", $target, "middleware");
$this->router->__invoke($this->request->reveal(), $this->response->reveal(), $this->next);
$this->request->withAttribute("pathVariables", $variables)->shouldHaveBeenCalled();
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// No Matching Routes // No Matching Routes