Last active
January 4, 2016 05:39
-
-
Save duskohu/8576500 to your computer and use it in GitHub Desktop.
Modular Router
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Nas; | |
use Nette\Application\IRouter; | |
use Nette\Application\Request; | |
use Nette\Http\IRequest; | |
use Nette\Http\Url; | |
use Nette\Object; | |
class ModularRouter extends Object implements IRouter | |
{ | |
const MODULE_KEY = 'module'; | |
const SUBMODULE_KEY = 'submodule'; | |
const PRESENTER_KEY = 'presenter'; | |
const ACTION_KEY = 'action'; | |
const PARAMS_KEY = 'params'; | |
/** @var string Default submodule name for admin */ | |
private $adminModule = 'admin'; | |
/** @var string Default submodule name for front */ | |
private $frontModule = 'front'; | |
/** @var array */ | |
private $defaultAdminModuleMask = array( | |
self::MODULE_KEY => '', | |
self::SUBMODULE_KEY => '', | |
self::PRESENTER_KEY => 'homepage', | |
self::ACTION_KEY => 'default', | |
); | |
/** @var array */ | |
private $defaultFrontModuleMask = array( | |
self::MODULE_KEY => '', | |
self::SUBMODULE_KEY => '', | |
self::PRESENTER_KEY => 'homepage', | |
self::ACTION_KEY => 'default', | |
); | |
/** @var bool Use slash on end of url */ | |
private $useSlash = FALSE; | |
/** @var array */ | |
private $presenterParamsMask = array(); | |
/** | |
* @param array $defaultAdminModuleMask | |
* @param array $defaultFrontModuleMask | |
* @throws \InvalidArgumentException | |
*/ | |
public function __construct($defaultAdminModuleMask, $defaultFrontModuleMask) | |
{ | |
if (!array_key_exists(self::MODULE_KEY, $defaultAdminModuleMask)) { | |
throw new \InvalidArgumentException("\$defaultAdminModuleMask must contains '" . self::MODULE_KEY . "' name."); | |
} | |
if (!array_key_exists(self::MODULE_KEY, $defaultAdminModuleMask)) { | |
throw new \InvalidArgumentException("\$defaultFrontModuleMask must contains '" . self::MODULE_KEY . "' name."); | |
} | |
$this->defaultAdminModuleMask[self::MODULE_KEY] = mb_strtolower($defaultAdminModuleMask[self::MODULE_KEY]); | |
$this->defaultAdminModuleMask[self::SUBMODULE_KEY] = $this->adminModule; | |
if (array_key_exists(self::PRESENTER_KEY, $defaultAdminModuleMask)) { | |
$this->defaultAdminModuleMask[self::PRESENTER_KEY] = mb_strtolower($defaultAdminModuleMask[self::PRESENTER_KEY]); | |
} | |
if (array_key_exists(self::ACTION_KEY, $defaultAdminModuleMask)) { | |
$this->defaultAdminModuleMask[self::ACTION_KEY] = mb_strtolower($defaultAdminModuleMask[self::ACTION_KEY]); | |
} | |
$this->defaultFrontModuleMask[self::MODULE_KEY] = mb_strtolower($defaultFrontModuleMask[self::MODULE_KEY]); | |
$this->defaultFrontModuleMask[self::SUBMODULE_KEY] = $this->frontModule; | |
if (array_key_exists(self::PRESENTER_KEY, $defaultFrontModuleMask)) { | |
$this->defaultFrontModuleMask[self::PRESENTER_KEY] = mb_strtolower($defaultFrontModuleMask[self::PRESENTER_KEY]); | |
} | |
if (array_key_exists(self::ACTION_KEY, $defaultFrontModuleMask)) { | |
$this->defaultFrontModuleMask[self::ACTION_KEY] = mb_strtolower($defaultFrontModuleMask[self::ACTION_KEY]); | |
} | |
} | |
/** | |
* @param string $name | |
* @param array $value | |
* @return $this | |
*/ | |
public function addPresenterParamsMask($name, array $value) | |
{ | |
$this->presenterParamsMask[mb_strtolower($name)] = $value; | |
return $this; | |
} | |
/** | |
* @param bool $value | |
* @return $this | |
*/ | |
public function setUseSlash($value = TRUE) | |
{ | |
$this->useSlash = $value; | |
return $this; | |
} | |
/** | |
* @param IRequest $httpRequest | |
* @return Request|NULL | |
*/ | |
public function match(IRequest $httpRequest) | |
{ | |
$path = $httpRequest->getUrl()->getPathInfo(); | |
// Add front module if not exist | |
if (substr($path, 0, strlen($this->adminModule)) !== $this->adminModule) { | |
$path = $this->frontModule . '/' . $path; | |
} | |
// Get <module>, <submodule>, <presenter>, <action>, <params> | |
$urlPattern = '/^'; | |
$urlPattern .= '(?P<' . self::SUBMODULE_KEY . '>[^\/]+)[\/]*'; | |
$urlPattern .= '(?P<' . self::MODULE_KEY . '>[^\/]+)*[\/]*'; | |
$urlPattern .= '(?P<' . self::PRESENTER_KEY . '>[^\/]+)*[\/]*'; | |
$urlPattern .= '(?P<' . self::ACTION_KEY . '>[^\/]+)*[\/]*'; | |
$urlPattern .= '(?P<' . self::PARAMS_KEY . '>.+)'; | |
$urlPattern .= '*/'; | |
preg_match($urlPattern, $path, $urlMatch); | |
// Set default module mask by submodule | |
if ($urlMatch[self::SUBMODULE_KEY] == $this->adminModule) { | |
$moduleMask = $this->defaultAdminModuleMask; | |
} else { | |
$moduleMask = $this->defaultFrontModuleMask; | |
} | |
// Set module mask values from url | |
if (isset($urlMatch[self::MODULE_KEY])) { | |
$moduleMask[self::MODULE_KEY] = $urlMatch[self::MODULE_KEY]; | |
} | |
if (isset($urlMatch[self::SUBMODULE_KEY])) { | |
$moduleMask[self::SUBMODULE_KEY] = $urlMatch[self::SUBMODULE_KEY]; | |
} | |
if (isset($urlMatch[self::PRESENTER_KEY])) { | |
$moduleMask[self::PRESENTER_KEY] = $urlMatch[self::PRESENTER_KEY]; | |
} | |
if (isset($urlMatch[self::ACTION_KEY])) { | |
$moduleMask[self::ACTION_KEY] = $urlMatch[self::ACTION_KEY]; | |
} | |
// Process params | |
$urlParams = array(); | |
if (isset($urlMatch[self::PARAMS_KEY])) { | |
$urlParams = explode('/', rtrim($urlMatch[self::PARAMS_KEY], '/')); | |
} | |
// Process presenter params mask | |
$presenterParamsMask = array(); | |
$moduleMaskPath = implode(':', $moduleMask); | |
if (array_key_exists($moduleMaskPath, $this->presenterParamsMask)) { | |
$presenterParamsMask = $this->presenterParamsMask[$moduleMaskPath]; | |
} | |
if (count($urlParams) > count($presenterParamsMask)) { | |
return NULL; | |
} | |
// Set params | |
$params = array(); | |
$params[self::ACTION_KEY] = $moduleMask[self::ACTION_KEY]; | |
unset($moduleMask[self::ACTION_KEY]); | |
$i = 0; | |
foreach ($presenterParamsMask as $name => $value) { | |
if (array_key_exists($i, $urlParams)) { | |
$params[$name] = $urlParams[$i]; | |
} else { | |
$params[$name] = $value; | |
} | |
$i++; | |
} | |
$params = array_merge($params, $httpRequest->getQuery()); | |
$presenter = implode(':', $moduleMask); | |
return new Request( | |
$presenter, | |
$httpRequest->getMethod(), | |
$params, | |
$httpRequest->getPost(), | |
$httpRequest->getFiles(), | |
array(Request::SECURED => $httpRequest->isSecured()) | |
); | |
} | |
/** | |
* Constructs absolute URL from Request object. | |
* @param Request $appRequest | |
* @param Url $refUrl | |
* @return NULL|string | |
*/ | |
function constructUrl(Request $appRequest, Url $refUrl) | |
{ | |
$params = $appRequest->getParameters(); | |
// Get <module>, <submodule>, <presenter>, <action>, <params> | |
$urlMatch = array(); | |
list($urlMatch[self::MODULE_KEY], $urlMatch[self::SUBMODULE_KEY], $urlMatch[self::PRESENTER_KEY]) = explode(':', mb_strtolower($appRequest->presenterName)); | |
// Set default module mask by submodule | |
if ($urlMatch[self::SUBMODULE_KEY] == $this->adminModule) { | |
$moduleMask = $this->defaultAdminModuleMask; | |
} else { | |
$moduleMask = $this->defaultFrontModuleMask; | |
} | |
// Set module mask values from url | |
if (isset($urlMatch[self::MODULE_KEY])) { | |
$moduleMask[self::MODULE_KEY] = $urlMatch[self::MODULE_KEY]; | |
} | |
if (isset($urlMatch[self::SUBMODULE_KEY])) { | |
$moduleMask[self::SUBMODULE_KEY] = $urlMatch[self::SUBMODULE_KEY]; | |
} | |
if (isset($urlMatch[self::PRESENTER_KEY])) { | |
$moduleMask[self::PRESENTER_KEY] = $urlMatch[self::PRESENTER_KEY]; | |
} | |
$moduleMask[self::ACTION_KEY] = $params[self::ACTION_KEY]; | |
unset($params[self::ACTION_KEY]); | |
// Process presenter params mask | |
$presenterParamsMask = array(); | |
$moduleMaskPath = implode(':', $moduleMask); | |
if (array_key_exists($moduleMaskPath, $this->presenterParamsMask)) { | |
$presenterParamsMask = $this->presenterParamsMask[$moduleMaskPath]; | |
} | |
$urlParams = array(); | |
foreach ($presenterParamsMask as $name => $value) { | |
if (array_key_exists($name, $params)) { | |
if ($params[$name] != '' && $params[$name] != NULL) { | |
$urlParams[$name] = $params[$name]; | |
} | |
unset($params[$name]); | |
} else { | |
if ($value != '' && $value != NULL) { | |
$urlParams[$name] = $value; | |
} | |
} | |
} | |
if (count($urlParams) == 0) { | |
if ($moduleMask[self::SUBMODULE_KEY] == $this->adminModule) { | |
if ($this->defaultAdminModuleMask[self::ACTION_KEY] == $moduleMask[self::ACTION_KEY]) { | |
unset($moduleMask[self::ACTION_KEY]); | |
if ($this->defaultAdminModuleMask[self::PRESENTER_KEY] == $moduleMask[self::PRESENTER_KEY]) { | |
unset($moduleMask[self::PRESENTER_KEY]); | |
if ($this->defaultAdminModuleMask[self::MODULE_KEY] == $moduleMask[self::MODULE_KEY]) { | |
unset($moduleMask[self::MODULE_KEY]); | |
} | |
} | |
} | |
} | |
if ($moduleMask[self::SUBMODULE_KEY] == $this->frontModule) { | |
if ($this->defaultFrontModuleMask[self::ACTION_KEY] == $moduleMask[self::ACTION_KEY]) { | |
unset($moduleMask[self::ACTION_KEY]); | |
if ($this->defaultFrontModuleMask[self::PRESENTER_KEY] == $moduleMask[self::PRESENTER_KEY]) { | |
unset($moduleMask[self::PRESENTER_KEY]); | |
if ($this->defaultFrontModuleMask[self::MODULE_KEY] == $moduleMask[self::MODULE_KEY]) { | |
unset($moduleMask[self::MODULE_KEY]); | |
} | |
} | |
} | |
} | |
} | |
if ($urlMatch[self::SUBMODULE_KEY] != $this->adminModule) { | |
unset($moduleMask[self::SUBMODULE_KEY]); | |
} else { | |
if (isset($moduleMask[self::MODULE_KEY]) && isset($moduleMask[self::SUBMODULE_KEY])) { | |
$module = $moduleMask[self::MODULE_KEY]; | |
$submodule = $moduleMask[self::SUBMODULE_KEY]; | |
$moduleMask[self::MODULE_KEY] = $submodule; | |
$moduleMask[self::SUBMODULE_KEY] = $module; | |
} | |
} | |
$urlPath = implode('/', $moduleMask); | |
if (!empty($urlParams)) { | |
$urlPath .= '/' . implode('/', $urlParams); | |
} | |
if ($urlPath != '') { | |
if (!empty($params) || $this->useSlash == TRUE) { | |
$urlPath .= '/'; | |
} | |
} | |
$url = new Url($refUrl->getBaseUrl() . $urlPath); | |
$url->setQuery($params); | |
return $url->getAbsoluteUrl(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App; | |
use Nas\ModularRouter; | |
use Nette; | |
use Nette\Application\Routers\RouteList; | |
/** | |
* Router factory. | |
*/ | |
class RouterFactory | |
{ | |
/** | |
* @return \Nette\Application\IRouter | |
*/ | |
public function createRouter() | |
{ | |
$router = new RouteList(); | |
// Set default module name for admin module | |
$defaultAdminModuleMask = array( | |
ModularRouter::MODULE_KEY => 'cms' | |
); | |
// Set default module name for front module | |
$defaultFrontModuleMask = array( | |
ModularRouter::MODULE_KEY => 'cms' | |
); | |
$modularRouter = new ModularRouter($defaultAdminModuleMask, $defaultFrontModuleMask); | |
// Set params mask for specific presenter >> action | |
$modularRouter->addPresenterParamsMask('cms:front:homepage:edit', array('id' => NULL)) | |
->addPresenterParamsMask('cms:front:homepage:default', array('id' => '123', 'name' => 'my-name')) | |
->setUseSlash(TRUE); | |
$router[] = $modularRouter; | |
return $router; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment