diff --git a/README.md b/README.md index a3df4d3..3375724 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ If you want a great new feature or experience any issues what-so-ever, please fe - [Namespaces](#namespaces) - [Subdomain-routing](#subdomain-routing) - [Route prefixes](#route-prefixes) + - [Partial groups](#partial-groups) - [Form Method Spoofing](#form-method-spoofing) - [Accessing The Current Route](#accessing-the-current-route) - [Other examples](#other-examples) @@ -79,7 +80,6 @@ If you want a great new feature or experience any issues what-so-ever, please fe - [Bootmanager: loading routes dynamically](#bootmanager-loading-routes-dynamically) - [Adding routes manually](#adding-routes-manually) - [Parameters](#parameters) - - [Custom default regex for matching parameters](#custom-default-regex-for-matching-parameters) - [Extending](#extending) - [Credits](#credits) @@ -358,7 +358,7 @@ SimpleRouter::any('foo', function() { }); ``` -We've created a simple method which matches `GET` and `POST` which is most commenly used: +We've created a simple method which matches `GET` and `POST` which is most commonly used: ```php SimpleRouter::form('foo', function() { @@ -542,7 +542,7 @@ SimpleRouter::group(['namespace' => 'Admin'], function () { }); ``` -### Subdomain-routing +### Sub domain-routing Route groups may also be used to handle sub-domain routing. Sub-domains may be assigned route parameters just like route URIs, allowing you to capture a portion of the sub-domain for usage in your route or controller. The sub-domain may be specified using the `domain` key on the group attribute array: @@ -566,6 +566,29 @@ SimpleRouter::group(['prefix' => '/admin'], function () { }); ``` +## Partial groups + +Partial router groups has the same benefits as a normal group, but supports parameters and are only rendered once the url has matched. + +This can be extremely useful in situations, where you only want special routes to be added, when a certain criteria or logic has been met. + +**NOTE:** Use partial groups with caution as routes added within are only rendered and available once the url of the partial-group has matched. This can cause `url()` not to find urls for the routes added within. + +**Example:** + +```php +SimpleRouter::partialGroup('/admin/{applicationId}', function ($applicationId) { + + SimpleRouter::get('/', function($applicationId) { + + // Matches The "/admin/applicationId" URL + + }); + +}); +``` + + ## Form Method Spoofing HTML forms do not support `PUT`, `PATCH` or `DELETE` actions. So, when defining `PUT`, `PATCH` or `DELETE` routes that are called from an HTML form, you will need to add a hidden `_method` field to the form. The value sent with the `_method` field will be used as the HTTP request method: diff --git a/src/Pecee/SimpleRouter/Route/IPartialGroupRoute.php b/src/Pecee/SimpleRouter/Route/IPartialGroupRoute.php new file mode 100644 index 0000000..c1ddd4d --- /dev/null +++ b/src/Pecee/SimpleRouter/Route/IPartialGroupRoute.php @@ -0,0 +1,8 @@ +getCallback(); if ($callback === null) { - return; + return null; } /* Render callback function */ @@ -154,14 +155,14 @@ abstract class Route implements IRoute $urlRegex = preg_quote($route, '/'); } - if (preg_match('/^' . $urlRegex . '\/?$/u', $url, $matches) > 0) { + if (preg_match(sprintf($this->urlRegex, $urlRegex), $url, $matches) > 0) { $values = []; if (isset($parameters[1]) === true) { /* Only take matched parameters with name */ - foreach ($parameters[1] as $name) { + foreach ((array)$parameters[1] as $name) { $values[$name] = (isset($matches[$name]) && $matches[$name] !== '') ? $matches[$name] : null; } } diff --git a/src/Pecee/SimpleRouter/Route/RouteGroup.php b/src/Pecee/SimpleRouter/Route/RouteGroup.php index e6397b1..b1e687b 100644 --- a/src/Pecee/SimpleRouter/Route/RouteGroup.php +++ b/src/Pecee/SimpleRouter/Route/RouteGroup.php @@ -150,11 +150,11 @@ class RouteGroup extends Route implements IGroupRoute $this->setPrefix($values['prefix'] . $this->prefix); } - if (isset($values['exceptionHandler'])) { + if ($merge === false && isset($values['exceptionHandler'])) { $this->setExceptionHandlers((array)$values['exceptionHandler']); } - if (isset($values['domain'])) { + if ($merge === false && isset($values['domain'])) { $this->setDomains((array)$values['domain']); } diff --git a/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php b/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php new file mode 100644 index 0000000..3790c0d --- /dev/null +++ b/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php @@ -0,0 +1,36 @@ +prefix !== null) { + /* Parse parameters from current route */ + $parameters = $this->parseParameters($this->prefix, $url); + + /* If no custom regular expression or parameters was found on this route, we stop */ + if ($parameters === null) { + return false; + } + + /* Set the parameters */ + $this->setParameters((array)$parameters); + } + + return $this->matchDomain($request); + } + +} \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/Router.php index ebe7672..8144a95 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/Router.php @@ -10,6 +10,7 @@ use Pecee\SimpleRouter\Exceptions\NotFoundHttpException; use Pecee\SimpleRouter\Route\IControllerRoute; use Pecee\SimpleRouter\Route\IGroupRoute; use Pecee\SimpleRouter\Route\ILoadableRoute; +use Pecee\SimpleRouter\Route\IPartialGroupRoute; use Pecee\SimpleRouter\Route\IRoute; class Router @@ -129,15 +130,27 @@ class Router $route = $routes[$i]; + if ($parent !== null) { + + /* Add the parent route */ + $route->setParent($parent); + + /* Add/merge parent settings with child */ + $route->setSettings($parent->toArray(), true); + + } + + if ($group !== null) { + + /* Add the parent group */ + $route->setGroup($group); + } + /* @var $route IGroupRoute */ if ($route instanceof IGroupRoute) { $group = $route; - $this->processingRoute = true; - $route->renderRoute($this->request); - $this->processingRoute = false; - if ($route->matchRoute($url, $this->request) === true) { /* Add exception handlers */ @@ -146,23 +159,20 @@ class Router $exceptionHandlers += $route->getExceptionHandlers(); } + /* Only render partial group if it matches */ + if ($route instanceof IPartialGroupRoute) { + $this->processingRoute = true; + $route->renderRoute($this->request); + $this->processingRoute = false; + } + } - } - - if ($group !== null) { - - /* Add the parent group */ - $route->setGroup($group); - } - - if ($parent !== null) { - - /* Add the parent route */ - $route->setParent($parent); - - /* Add/merge parent settings with child */ - $route->setSettings($parent->toArray(), true); + if (($route instanceof IPartialGroupRoute) === false) { + $this->processingRoute = true; + $route->renderRoute($this->request); + $this->processingRoute = false; + } } if ($route instanceof ILoadableRoute) { @@ -258,9 +268,8 @@ class Router if ($rewriteUrl !== null && $rewriteUrl !== $url) { unset($this->processedRoutes[$i]); $this->processedRoutes = array_values($this->processedRoutes); - $this->routeRequest(true); - return; + return $this->routeRequest(true); } /* Render route */ @@ -268,8 +277,6 @@ class Router $this->request->setLoadedRoute($route); return $route->renderRoute($this->request); - - break; } } @@ -293,12 +300,15 @@ class Router $this->handleException(new NotFoundHttpException($message, 404)); } + + return null; } /** * @param \Exception $e * @throws HttpException * @throws \Exception + * @return string */ protected function handleException(\Exception $e) { @@ -335,9 +345,8 @@ class Router if ($rewriteUrl !== null && $rewriteUrl !== $url) { unset($this->exceptionHandlers[$i]); $this->exceptionHandlers = array_values($this->exceptionHandlers); - $this->routeRequest(true); - return; + return $this->routeRequest(true); } } } diff --git a/src/Pecee/SimpleRouter/SimpleRouter.php b/src/Pecee/SimpleRouter/SimpleRouter.php index 84734d3..2c545a2 100644 --- a/src/Pecee/SimpleRouter/SimpleRouter.php +++ b/src/Pecee/SimpleRouter/SimpleRouter.php @@ -16,6 +16,7 @@ use Pecee\Http\Response; use Pecee\SimpleRouter\Exceptions\HttpException; use Pecee\SimpleRouter\Exceptions\NotFoundHttpException; use Pecee\SimpleRouter\Route\IRoute; +use Pecee\SimpleRouter\Route\RoutePartialGroup; use Pecee\SimpleRouter\Route\RouteController; use Pecee\SimpleRouter\Route\RouteGroup; use Pecee\SimpleRouter\Route\RouteResource; @@ -171,14 +172,41 @@ class SimpleRouter */ public static function group(array $settings = [], \Closure $callback) { + if (is_callable($callback) === false) { + throw new \InvalidArgumentException('Invalid callback provided. Only functions or methods supported'); + } + $group = new RouteGroup(); $group->setCallback($callback); $group->setSettings($settings); + static::router()->addRoute($group); + + return $group; + } + + /** + * Special group that has the same benefits as group but supports + * parameters and which are only rendered when the url matches. + * + * @param string $url + * @param array $settings + * @param \Closure $callback + * @throws \InvalidArgumentException + * @return RoutePartialGroup + */ + public static function partialGroup($url, \Closure $callback, array $settings = []) + { if (is_callable($callback) === false) { throw new \InvalidArgumentException('Invalid callback provided. Only functions or methods supported'); } + $settings['prefix'] = $url; + + $group = new RoutePartialGroup(); + $group->setSettings($settings); + $group->setCallback($callback); + static::router()->addRoute($group); return $group; diff --git a/test/RouterPartialGroupTest.php b/test/RouterPartialGroupTest.php new file mode 100644 index 0000000..9c805e5 --- /dev/null +++ b/test/RouterPartialGroupTest.php @@ -0,0 +1,29 @@ +assertEquals('param1', $result1); + $this->assertEquals('param2', $result2); + } + +} \ No newline at end of file