diff --git a/README.md b/README.md index 84dc879..a0d974c 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ This router is heavily inspired by the Laravel 5.* router, so anything you find ### Basic example ```php -using \Pecee\SimpleRouter; +use Pecee\SimpleRouter\SimpleRouter; /* * This route will match the url /v1/services/answers/1/ @@ -63,13 +63,14 @@ SimpleRouter::group(['prefix' => 'v1', 'middleware' => '\MyWebsite\Middleware\So ### Doing it the object oriented (hardcore) way -The ```Router``` class is just a simple helper class that knows how to communicate with the ```SimpleRouter``` class. If you are up for a challenge, want the full control or simply just want to create your own ```Router``` helper class, this example is for you. +The ```SimpleRouter``` class referenced in the previous example, is just a simple helper class that knows how to communicate with the ```RouterBase``` class. +If you are up for a challenge, want the full control or simply just want to create your own ```Router``` helper class, this example is for you. ```php -use \Pecee\SimpleRouter; -use \Pecee\SimpleRouter\Router; +use \Pecee\SimpleRouter\RouterBase; +use \Pecee\SimpleRouter\RouterRoute; -$router = Router::getInstance(); +$router = RouterBase::getInstance(); $route = new RouterRoute('/answer/1', function() { die('this callback will match /answer/1'); @@ -83,6 +84,78 @@ $route->setPrefix('v1'); $router->addRoute($route); ``` +This is a simple example of an integration into a framework. + +The framework has it's own ```Router``` class which inherits from the ```SimpleRouter``` class. This allows the framework to add custom functionality. + +```php +namespace MyProject; + +use Pecee\Handler\ExceptionHandler; +use Pecee\SimpleRouter\SimpleRouter; + +class Router extends SimpleRouter { + + protected static $exceptionHandlers = array(); + + public static function start() { + + Debug::getInstance()->add('Router initialised.'); + + // Load routes.php + $file = $_ENV['basePath'] . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'routes.php'; + if(file_exists($file)) { + require_once $file; + } + + // Init locale settings + Locale::getInstance(); + + // Set default namespace + $defaultNamespace = '\\'.Registry::getInstance()->get('AppName') . '\\Controller'; + + // Handle exceptions + try { + parent::start($defaultNamespace); // TODO: Change the autogenerated stub + } catch(\Exception $e) { + /* @var $handler ExceptionHandler */ + foreach(self::$exceptionHandlers as $handler) { + $class = new $handler(); + $class->handleError($e); + } + + throw $e; + } + } + + public static function addExceptionHandler($handler) { + self::$exceptionHandlers[] = $handler; + } + +} +``` + +This is a basic example of a ```helper.php``` for generating urls. + +```php +use Pecee\SimpleRouter\RouterBase; +function url($controller, $parameters = null, $getParams = null) { + RouterBase::getInstance()->getRoute($controller, $parameters, $getParams); +} +``` + +In ```routes.php``` we have added this route: + +```Router::get('/item/{id}', 'myController@show');``` + +In the template we call: + +```url('myController@show', ['id' => 22], ['category' => 'shoes']);``` + +Result url is: + +```/item/22?category=shoes``` + ## Documentation While I work on a better documentation, please refer to the Laravel 5 routing documentation here: diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/RouterBase.php similarity index 70% rename from src/Pecee/SimpleRouter/Router.php rename to src/Pecee/SimpleRouter/RouterBase.php index eef39c2..75e97c3 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/RouterBase.php @@ -1,12 +1,15 @@ routes = array(); $this->backstack = array(); + $this->controllerUrlMap = array(); $this->requestUri = rtrim($_SERVER['REQUEST_URI'], '/'); $this->requestMethod = strtolower(isset($_GET['_method']) ? $_GET['_method'] : $_SERVER['REQUEST_METHOD']); } public function addRoute(RouterEntry $route) { - // Add default namespace - if(!$route->getNamespace() && $this->defaultControllerNamespace !== null) { - $route->setNamespace($this->defaultControllerNamespace); - } - if($this->currentRoute !== null) { $this->backstack[] = $route; } else { @@ -33,12 +32,6 @@ class Router { } } - public function route($url, $callback) { - $route = new RouterRoute($url, $callback); - $this->addRoute($route); - return $route; - } - protected function loadClass($name) { if(!class_exists($name)) { throw new RouterException(sprintf('Class %s does not exist', $name)); @@ -55,6 +48,11 @@ class Router { $this->loadClass($route->getMiddleware()); } + // Add default namespace + if(!$route->getNamespace() && $this->defaultControllerNamespace !== null) { + $route->setNamespace($this->defaultControllerNamespace); + } + if(is_object($route->getCallback()) && is_callable($route->getCallback())) { // When the callback is a function @@ -64,6 +62,7 @@ class Router { // When the callback is a method $controller = explode('@', $route->getCallback()); + $class = $route->getNamespace() . '\\' . $controller[0]; $class = $this->loadClass($class); @@ -80,7 +79,7 @@ class Router { } } - protected function renderBackstack(array $routes, &$settings, &$prefixes) { + protected function processRoutes(array $routes, array &$settings = array(), array &$prefixes = array()) { // Loop through each route-request /* @var $route RouterEntry */ foreach($routes as $route) { @@ -90,10 +89,16 @@ class Router { array_push($prefixes, $route->getPrefix()); } - // If the route is a group + $route->setSettings($settings); + if($route instanceof RouterRoute) { - $route->setSettings($settings); - $route->setUrl( '/' . join('/', $prefixes) . $route->getUrl() ); + if(is_array($prefixes) && count($prefixes)) { + $route->setUrl( '/' . join('/', $prefixes) . $route->getUrl() ); + } + + if(stripos($route->getCallback(), '@') !== false) { + $this->controllerUrlMap[$route->getCallback()] = $route; + } } // Stop if the route matches @@ -102,38 +107,20 @@ class Router { $this->renderRoute($route); } - // Remove itself from backstack - array_shift($this->backstack); + if(count($this->backstack)) { + // Remove itself from backstack + array_shift($this->backstack); - // Route any routes added to the backstack - $this->renderBackstack($this->backstack, $settings, $prefixes); + // Route any routes added to the backstack + $this->processRoutes($this->backstack, $settings, $prefixes); + } } } public function routeRequest() { // Loop through each route-request - /* @var $route RouterEntry */ - foreach($this->routes as $route) { - // Reset variables - $settings = array(); - $prefixes = array(); - - $settings = array_merge($settings, $route->getMergeableSettings()); - - if($route->getPrefix()) { - array_push($prefixes, $route->getPrefix()); - } - - // Stop if the route matches - $route = $route->getRoute($this->requestMethod, $this->requestUri); - if($route) { - $this->renderRoute($route); - } - - // Route any routes added to the backstack - $this->renderBackstack($this->backstack, $settings, $prefixes); - } + $this->processRoutes($this->routes); } /** @@ -192,6 +179,36 @@ class Router { return $this->routes; } + public function getRoute($controller, $parameters = null, $getParams = null) { + /* @var $route RouterRoute */ + foreach($this->controllerUrlMap as $c => $route) { + $params = $route->getParameters(); + + if(strtolower($c) === strtolower($controller)) { + + $url = $route->getUrl(); + + $i = 0; + foreach($params as $param => $value) { + $value = (isset($parameters[$param])) ? $parameters[$param] : $value; + $url = str_ireplace('{' . $param. '}', $value, $route->getUrl()); + $i++; + } + + $p = ''; + if($getParams !== null) { + $p = '?'.Url::arrayToParams($getParams); + } + + $url .= $p; + + return $url; + } + } + + return '/'; + } + public static function getInstance() { if(self::$instance === null) { self::$instance = new self(); diff --git a/src/Pecee/SimpleRouter/RouterEntry.php b/src/Pecee/SimpleRouter/RouterEntry.php index 8e45f7b..2aabbbe 100644 --- a/src/Pecee/SimpleRouter/RouterEntry.php +++ b/src/Pecee/SimpleRouter/RouterEntry.php @@ -27,18 +27,6 @@ abstract class RouterEntry { $this->parameters = array(); } - protected function parseParameter($path) { - $parameters = array(); - - preg_match('/{([A-Za-z\-\_]*?)}/is', $path, $parameters); - - if(isset($parameters[1]) && count($parameters[1]) > 0) { - return $parameters[1]; - } - - return null; - } - /** * @param string $callback * @return self; diff --git a/src/Pecee/SimpleRouter/RouterRoute.php b/src/Pecee/SimpleRouter/RouterRoute.php index 25a48fc..9617f47 100644 --- a/src/Pecee/SimpleRouter/RouterRoute.php +++ b/src/Pecee/SimpleRouter/RouterRoute.php @@ -11,12 +11,36 @@ class RouterRoute extends RouterEntry { public function __construct($url, $callback) { parent::__construct(); - $this->url = $url; - $this->callback = $callback; + $this->setUrl($url); + $this->setCallback($callback); $this->settings['aliases'] = array(); } + protected function parseParameters($url) { + $parameters = array(); + + preg_match_all('/{([A-Za-z\-\_]*?)}/is', $url, $parameters); + + if(isset($parameters[1]) && count($parameters[1]) > 0) { + return $parameters[1]; + } + + return null; + } + + protected function parseParameter($path) { + $parameters = array(); + + preg_match('/{([A-Za-z\-\_]*?)}/is', $path, $parameters); + + if(isset($parameters[1]) && count($parameters[1]) > 0) { + return $parameters[1]; + } + + return null; + } + public function getRoute($requestMethod, &$url) { // Check if request method is allowed @@ -73,6 +97,15 @@ class RouterRoute extends RouterEntry { * @return self */ public function setUrl($url) { + + $parameters = $this->parseParameters($url); + + if($parameters !== null) { + foreach($parameters as $param) { + $this->parameters[$param] = ''; + } + } + $this->url = $url; return $this; } diff --git a/src/Pecee/SimpleRouter.php b/src/Pecee/SimpleRouter/SimpleRouter.php similarity index 78% rename from src/Pecee/SimpleRouter.php rename to src/Pecee/SimpleRouter/SimpleRouter.php index 327d709..f441315 100644 --- a/src/Pecee/SimpleRouter.php +++ b/src/Pecee/SimpleRouter/SimpleRouter.php @@ -7,16 +7,12 @@ * This class is added so calls can be made staticly like Router::get() making the code look more pretty. */ -namespace Pecee; - -use Pecee\SimpleRouter\RouterGroup; -use Pecee\SimpleRouter\RouterRoute; -use Pecee\SimpleRouter\Router; +namespace Pecee\SimpleRouter; class SimpleRouter { - public static function init($defaultNamespace = null) { - $router = Router::GetInstance(); + public static function start($defaultNamespace = null) { + $router = RouterBase::GetInstance(); $router->setDefaultControllerNamespace($defaultNamespace); $router->routeRequest(); } @@ -25,7 +21,7 @@ class SimpleRouter { $route = new RouterRoute($url, $callback); $route->addRequestType(RouterRoute::REQUEST_TYPE_GET); - $router = Router::GetInstance(); + $router = RouterBase::getInstance(); $router->addRoute($route); return $route; @@ -35,7 +31,7 @@ class SimpleRouter { $route = new RouterRoute($url, $callback); $route->addRequestType(RouterRoute::REQUEST_TYPE_POST); - $router = Router::GetInstance(); + $router = RouterBase::getInstance(); $router->addRoute($route); return $route; @@ -45,7 +41,7 @@ class SimpleRouter { $route = new RouterRoute($url, $callback); $route->addRequestType(RouterRoute::REQUEST_TYPE_PUT); - $router = Router::GetInstance(); + $router = RouterBase::getInstance(); $router->addRoute($route); return $route; @@ -55,7 +51,7 @@ class SimpleRouter { $route = new RouterRoute($url, $callback); $route->addRequestType(RouterRoute::REQUEST_TYPE_DELETE); - $router = Router::GetInstance(); + $router = RouterBase::getInstance(); $router->addRoute($route); return $route; @@ -69,7 +65,7 @@ class SimpleRouter { $group->setSettings($settings); } - $router = Router::GetInstance(); + $router = RouterBase::getInstance(); $router->addRoute($group); return $group; @@ -81,10 +77,14 @@ class SimpleRouter { $route->addRequestType($requestType); } - $router = Router::GetInstance(); + $router = RouterBase::getInstance(); $router->addRoute($route); return $route; } + public static function ressource($controller, $settings = array()) { + // not yet implemented + } + } \ No newline at end of file