Skip to content

Commit

Permalink
routing cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
jarischaefer committed Apr 3, 2016
1 parent 09d1bd6 commit 7c1ef91
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 117 deletions.
209 changes: 101 additions & 108 deletions src/Jarischaefer/HalApi/Helpers/RouteHelper.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
<?php namespace Jarischaefer\HalApi\Helpers;

use Exception;
use Illuminate\Http\Request;
use Illuminate\Routing\Route;
use Illuminate\Routing\Router;
use InvalidArgumentException;
use Jarischaefer\HalApi\Controllers\HalApiControllerContract;
use ReflectionClass;
use RuntimeException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

Expand All @@ -19,6 +16,83 @@
class RouteHelper implements RouteHelperConstants
{

/**
* @var array
*/
private static $relationCache = [];
/**
* @var array
*/
private static $isValidActionNameCache = [];

/**
* Checks if a route is bound to an implementation of {@see HalApiControllerContract}
*
* @param Route $route
* @return bool
*/
public static function isValid(Route $route): bool
{
return self::isValidActionName($route->getActionName());
}

/**
* @param string $actionName
* @return bool
*/
public static function isValidActionName(string $actionName): bool
{
if (isset(self::$isValidActionNameCache[$actionName])) {
return self::$isValidActionNameCache[$actionName];
}

$split = explode(self::ACTION_NAME_DELIMITER, $actionName);
$isValid = empty($split) ? false : is_subclass_of($split[0], HalApiControllerContract::class);

return self::$isValidActionNameCache[$actionName] = $isValid;
}

/**
* @param Route $route
* @return string
*/
public static function relation(Route $route): string
{
$actionName = $route->getActionName();

if (isset(self::$relationCache[$actionName])) {
return self::$relationCache[$actionName];
}

/** @var HalApiControllerContract $class */
list($class, $method) = explode(self::ACTION_NAME_DELIMITER, $actionName);

return self::$relationCache[$actionName] = $class::getRelation($method);
}

/**
* @return callable
*/
public static function getModelBindingCallback(): callable
{
return function ($value) {
switch (\Request::getMethod()) {
case Request::METHOD_GET:
throw new NotFoundHttpException;
case Request::METHOD_POST:
throw new NotFoundHttpException;
case Request::METHOD_PUT:
return $value;
case Request::METHOD_PATCH:
throw new NotFoundHttpException;
case Request::METHOD_DELETE:
throw new NotFoundHttpException;
default:
return null;
}
};
}

/**
* Holds routes belonging to an action name.
*
Expand Down Expand Up @@ -46,19 +120,19 @@ class RouteHelper implements RouteHelperConstants

/**
* @param Router $router
* @return RouteHelper
*/
public function __construct(Router $router)
public static function make(Router $router): RouteHelper
{
$this->router = $router;
return new static($router);
}

/**
* @param Router $router
* @return RouteHelper
*/
public static function make(Router $router): RouteHelper
public function __construct(Router $router)
{
return new static($router);
$this->router = $router;
}

/**
Expand Down Expand Up @@ -183,7 +257,7 @@ public function pagination(string $uri, string $controller, string $method): Rou
*/
public function byAction(string $actionName): Route
{
if (array_key_exists($actionName, $this->byActionRouteCache)) {
if (isset($this->byActionRouteCache[$actionName])) {
return $this->byActionRouteCache[$actionName];
}

Expand All @@ -206,45 +280,33 @@ public function byAction(string $actionName): Route
*/
public function parent(Route $child): Route
{
// TODO reflection mess

$childUri = $child->getUri();

if (array_key_exists($childUri, $this->parentRouteCache)) {
if (isset($this->parentRouteCache[$childUri])) {
return $this->parentRouteCache[$childUri];
}

$lastSlash = strripos($childUri, '/');
$guessedParentUri = $lastSlash === FALSE ? '/' : substr($childUri, 0, $lastSlash);
$request = new Request;
$reflectionClass = new ReflectionClass($request);
$pathInfo = $reflectionClass->getProperty('pathInfo');
$pathInfo->setAccessible(true);
$requestUri = $reflectionClass->getProperty('requestUri');
$requestUri->setAccessible(true);

while (true) {
$pathInfo->setValue($request, $guessedParentUri);
$requestUri->setValue($request, $guessedParentUri);

try {
$route = $this->router->getRoutes()->match($request);

if ($route instanceof Route) {
/** @var Route $route */
foreach ($this->router->getRoutes() as $route) {
if (strcmp($route->getUri(), $guessedParentUri) === 0) {
return $this->parentRouteCache[$childUri] = $route;
}
} catch (Exception $e) {
if ($guessedParentUri == '/') {
break;
}
}
}

if ($guessedParentUri === '/') {
break;
}

// cut off another slash part (e.g. /users/{userid}/friends -> /users/{userid})
$guessedParentUri = substr($guessedParentUri, 0, strripos($guessedParentUri, '/'));

if ($guessedParentUri == '') {
$guessedParentUri = '/';
}
if ($guessedParentUri === '') {
$guessedParentUri = '/';
}
}

return $this->parentRouteCache[$childUri] = $child; // return the same route if no parent exists
Expand All @@ -261,7 +323,7 @@ public function subordinates(Route $parentRoute): array
{
$parentUri = $parentRoute->getUri();

if (array_key_exists($parentUri, $this->subordinateRouteCache)) {
if (isset($this->subordinateRouteCache[$parentUri])) {
return $this->subordinateRouteCache[$parentUri];
}

Expand All @@ -270,18 +332,17 @@ public function subordinates(Route $parentRoute): array

/** @var Route $route */
foreach ($this->router->getRoutes() as $route) {
$actionName = $route->getActionName();

if (!self::isValidActionName($actionName)) {
// if the route does not start with the same uri as the current route -> skip
if ($parentUri !== '/' && strpos($route->getUri(), $parentUri) !== 0) {
continue;
}

// if the route does not start with the same uri as the current route -> skip
if ($parentUri !== '/' && !starts_with($route->getUri(), $parentUri)) {
$actionName = $route->getActionName();

if (!self::isValidActionName($actionName)) {
continue;
}

// if route equals the parent route
if (strcmp($parentActionName, $actionName) !== 0) {
$children[] = $route;
}
Expand All @@ -290,72 +351,4 @@ public function subordinates(Route $parentRoute): array
return $this->subordinateRouteCache[$parentUri] = $children;
}

/**
* Checks if a route is bound to an implementation of {@see HalApiControllerContract}
*
* @param Route $route
* @return bool
*/
public static function isValid(Route $route): bool
{
return self::isValidActionName($route->getActionName());
}

/**
* @param string $actionName
* @return bool
*/
public static function isValidActionName(string $actionName): bool
{
// valid routes are backed by a controller (e.g. App\Http\Controllers\MyController@doSomething)
if (!str_contains($actionName, self::ACTION_NAME_DELIMITER)) {
return false;
}

$class = explode(self::ACTION_NAME_DELIMITER, $actionName)[0];

return is_subclass_of($class, HalApiControllerContract::class);
}

/**
* @param Route $route
* @return string
*/
public static function relation(Route $route): string
{
$actionName = $route->getActionName();

if (!self::isValidActionName($actionName)) {
throw new InvalidArgumentException('Invalid route');
}

/** @var HalApiControllerContract $class */
list($class, $method) = explode(self::ACTION_NAME_DELIMITER, $actionName);

return $class::getRelation($method);
}

/**
* @inheritdoc
*/
public static function getModelBindingCallback(): callable
{
return function ($value) {
switch (\Request::getMethod()) {
case Request::METHOD_GET:
throw new NotFoundHttpException;
case Request::METHOD_POST:
throw new NotFoundHttpException;
case Request::METHOD_PUT:
return $value;
case Request::METHOD_PATCH:
throw new NotFoundHttpException;
case Request::METHOD_DELETE:
throw new NotFoundHttpException;
default:
return null;
}
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,6 @@ public function dataFromArray(array $data): HalApiRepresentation
*/
public function link(string $relation, HalApiLink $link): HalApiRepresentation
{
if (!is_string($relation)) {
throw new InvalidArgumentException('relation must be a string, got: ' . gettype($relation));
}

$this->links[$relation] = $link;

return $this;
Expand Down Expand Up @@ -223,7 +219,7 @@ public function build(): array
$build = $this->root;

if ($this->autoSubordinateRoutes) {
if (!array_key_exists(self::SELF, $this->links)) {
if (!isset($this->links[self::SELF])) {
throw new RuntimeException('relation for self is not defined, cannot add subordinate routes');
}

Expand Down
7 changes: 3 additions & 4 deletions src/Jarischaefer/HalApi/Routing/HalApiLinkImpl.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<?php namespace Jarischaefer\HalApi\Routing;

use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Routing\Route;

/**
Expand Down Expand Up @@ -42,7 +41,7 @@ public function __construct(HalApiUrlGenerator $urlGenerator, Route $route, $par
$this->route = $route;
$this->parameters = is_array($parameters) ? $parameters : [$parameters];
$this->queryString = self::createQueryString($queryString);
$this->templated = self::evaluateTemplated($route, $urlGenerator, $queryString);
$this->templated = self::evaluateTemplated($route, $urlGenerator, $this->queryString);
$this->link = $urlGenerator->action($this->route->getActionName(), $this->templated ? $this->parameters : []);

if (!empty($this->queryString)) {
Expand Down Expand Up @@ -114,11 +113,11 @@ public function __toString(): string

/**
* @param Route $route
* @param UrlGenerator $urlGenerator
* @param HalApiUrlGenerator $urlGenerator
* @param string $queryString
* @return bool
*/
private static function evaluateTemplated(Route $route, UrlGenerator $urlGenerator, string $queryString): bool
private static function evaluateTemplated(Route $route, HalApiUrlGenerator $urlGenerator, string $queryString): bool
{
// Does the route have named parameters? http://example.com/users/{users}
if (count($route->parameterNames())) {
Expand Down

0 comments on commit 7c1ef91

Please sign in to comment.