nette/application: Modular Router
- ales.kafka
- Member | 34
Reasons and current status of RFC
Nette Route is one of the best features there is and I really like it from the beginning. It's easy, powerful and clean. I've always found it good enough to handle all I wanted. But after working on some big applications and also creating some light website without full Nette Framework (just Latte, DI and Http), I'm missing bigger modularity.
First example is slug in URL. It's really hard to find suitable way, how to handle such requests with all requisites. And it's tip of the iceberg. There are other cases that I would like to wrap into reusable modules that interact with presenter as little as possible. Likes of:
- /article/2015/02/01/title-as-slug-in-url–12456.html
- URL that contains signature created from other parts
- wrap params into encrypted and base64 encoded string
- encode auto_incremented ids into form, where id cannot be easily interpreted
Maybe I could come up with others, but you know what I mean by now.
I would like to point out, that this is not final proposal, just a work-in-progress. I didn't investigate how it could be implemented, only that proposed API can be implemented without BC. I also won't have time to work on the code until summer, but I still think it's very good idea worth sharing right away.
Current behaviour
Nette Router is really simple and outlines didn't change since the beginning. Configuration is done by string that carries information about parts of URL. Whether they are optional, allowed characters, mapping into arguments, etc. Router match URL if it is matched by internal regular expression from start to finish. It's probably more difficult than this, but let's not bother with details.
Proposed behaviour
Everything stays the same, but configuration can be separated into arbitrary number of modules. In final URL, these modules are compulsory separated by slash.
It took me while to come up with API that would really enhance Nette Router and still kept it simple. Let's see example of definition of route for aforementioned case 1.
We want to choose presenter (in this case article) and then apply module that
provides functionality of matching and constructing of URL such as this
2015/02/01/title-as-slug-in-url--12456.html
given article entity as
param. Also we would like to have default action and comment action
as well.
Let's build route to cover this example.
$router[] = new Route('/<presenter article>', new RouterModule/Article, '[<action=default>]', array(...));
And that would be it. Lovely part of this API is, that thanks to PHP
5.4 array optional module stays the same, you would just wraps it into array
like this: [new RouteModule/Article]
.
In terms of implementation, RouterModule/Article
would have to
implement interface Nette\Application\IRouterModule
. This interface
would be very similar to Nette\Application\IRouter
, but with some
alternations. Really sketchy draft of the interface:
namespace Nette\Application;
use Nette;
/**
* Router module.
*/
interface IRouterModule
{
/**
* @param Http\IRequest
* @param string $path Remaining part of path that is being matched by IRouter.
* @return ModuleRequest|NULL returns instance of ModuleRequest if it matched part of URL or NULL otherwise
*/
function match(Nette\Http\IRequest $httpRequest, $path);
/**
* Constructs part of URL
* @return string|NULL
*/
function constructUrl(Request $appRequest, Nette\Http\Url $refUrl);
}
Mentioned Nette\Application\ModuleRequest
needs some serious
thoughts how it would be implemented. Since it needs to signalize how big chunk
of path has been matched and also provide parameters for final
Nette\Application\Request
in case whole URL is matched by
underlying Route.
Things that could be considered in discussion and maybe improve nette application even further.
- Could presenter as whole require parameters? Like ArticlePresenter could be only instantiate with $article entity passed to constructor? $article entity would be provided by IRouterModule as one of the resulting parameters.
- Could IRouterModule handle response of application? Like #404 when it matches part of the URL, but didn't find appropriate entity?
What are your thoughts about this RFC?
- ales.kafka
- Member | 34
enumag wrote:
Can you explain why you don't use FILTER_IN and FILTER_OUT callbacks?
I don't like partial solutions. In this case due to configuring Route through multidimensional array. It feels so Nette 0.9.
I would like to have this modules to wrap functionality I use in every
project, but that could vary project by project. For example, constructor of
mentioned module RouterModule\Article
could take first parameter
that would specify form of the path. Like this:
%Y/%m/%title--%id.html
, sometimes I want this:
%id/%title.html
, etc.
- ales.kafka
- Member | 34
I suppose that could do the trick. That is to implement it in easiest way possible to offer me proposed functionality. But I wanted to share this idea, because it doesn't break the existing code and it would add simple but very powerful concept to Nette.