Skip to content

Instantly share code, notes, and snippets.

@duskohu
Last active January 4, 2016 05:39
Show Gist options
  • Save duskohu/8576500 to your computer and use it in GitHub Desktop.
Save duskohu/8576500 to your computer and use it in GitHub Desktop.
Modular Router
<?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();
}
}
<?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