Not so long ago came the
third version of this framework. Even before it was released, I did a
small review of new features. Then I went around the topic of routing and HMVC, but today I am ready to correct.
Part One Routing
So, the routing has changed a lot. More settings are not stored in the configuration file, but are set in the
bootstrap file code using the static
Route :: set method, which has the following syntax:
/**
* Stores a named route and returns it.
*
* @param string route name
* @param string URI pattern
* @param array regex patterns for route keys
* @return Route
*/
public static function set ($name, $uri, array $regex = NULL)
* This source code was highlighted with Source Code Highlighter .
This method returns a route, an object that stores information about the parameters that must be obtained from the
url and can tell if it is able to process a particular
url . The basic rule that was previously used to map most
url addresses to specific controller methods is now simply a default route that can be easily removed and set for example in the
bootstrap file:
Route:: set ( 'default' , '(<controller>(/<action>(/<id>)))' )
->defaults(array(
'controller' => 'welcome' ,
'action' => 'index' ,
));
* This source code was highlighted with Source Code Highlighter .
It should be noted that when determining compliance
url specific route, routes are tested in the order of addition, because the more general rules should be added after the more specific. In this case, the
default route is very general and if you decide to leave it, it is better to add all other routes in front of it.
Consider the parameters
Route :: set more closely. The first parameter,
$ name , is used to further identify the route, in particular, it should be passed to the
Route :: get method. The next parameter
$ uri is a template that determines how the
url should look suitable for this route. In this template, all parameters must be specified in triangular brackets (
controller ,
action and
id in the default route). All captured parameters are available via
Request-> param ($ name, $ default) . If any part of the
url is optional (including parameters), it needs to be enclosed in brackets. The final parameter of the
Route :: set method is the $ regex array with regular expressions that are tested for parameters. By default, all parameters are tested with the expression "[^ /.,;?] ++". If for some parameter this expression does not fit, you need to pass in the
$ regex array the appropriate expression with the key in the array corresponding to the parameter name. For example, for the default route it would be logical to specify the following array:
Route:: set ( 'default' , '(<controller>(/<action>(/<id>)))' , array(
'id' => '\d{1,10}' ,
))
* This source code was highlighted with Source Code Highlighter .
For each parameter, you can set default values that will be used if the parameter in the chalon is set as optional and not in the
url . This is done using the
defaults method, which is applied to Route objects and takes an array as a parameter, built on the same principle as the
$ regex parameter of the
Route :: set method.
')
Among the parameters that can be passed through the
url and
defaults method, there are 3 parameters with a special value. Their names are
controller ,
action and
directory . These parameters cannot be obtained through the
Request-> param method, but they are available directly from the
Request object. Those.
Request-> controller ,
Request-> action , and
Request-> directory .
Parameter
controller . The only mandatory of the three. Must be present either directly url, or set through the method
Route-> defaults . The name speaks for itself.
Action parameter Another parameter with a talking name. If not specified (or specified as an empty string), the default value “index” is taken.
The
directory parameter. Specifies the subdirectory of the
controller directory in which to search for the desired controller. If the file is in controller / tools / sidebars / bottom, then the class name should be
Controller_Tools_Sidebars_Bottom , and the parameters: controller = 'bottom', directory = 'tools / sidebars'.
To make it clearer, what opportunities open up with such an approach, I will give a few examples of routing rules.
Example 1Route:: set ( 'wigets' , 'wigets(/<action>(/<id>))' )
->defaults(array(
'controller' => 'wiget' ,
));
* This source code was highlighted with Source Code Highlighter .
To match this route, the address must begin on wigets, it can have 2 arbitrary parameters through the slash. There is no way to change the controller from the url, therefore the route is served by only one wiget controller.
Example 2Route:: set ( 'articles' , 'articles(/<action>(/<sorting>/<page>))' , array(
'sorting' => '(?:asc|desc)' ,
'page' => '\d{1,5}'
))
* This source code was highlighted with Source Code Highlighter .
The address should begin on articles, then there is an optional parameter action and 2 more parameters surrounded by only one brackets with the given regular expressions. The expression in brackets can be either omitted only completely or accepted. In this case, this means that if the address looks like / articles / list / asc /, then the asc parameter will not be accepted, because along with it there is another parameter in the same brackets that does not correspond to anything.
Example 3Route:: set ( 'articles' , 'articles(/<action>((/<id>)/<sorting>(<page>)))' , array(
'sorting' => '(?:name|date|age)' ,
'page' => '\d{1,5}' ,
))
* This source code was highlighted with Source Code Highlighter .
And here at all amazing things demonstrated. First, the id parameter is optional and is not at the end of the expression. This means that the addresses / articles / list / 154 / date and / articles / list / date will be correct. In addition, sorting is not separated from page by any separator, so url / articles / list / date20 will also be accepted.
Example 4Route:: set ( 'articles' , 'articles(,<action>((,<id>),<sorting>(<page>)))' , array(
'sorting' => '(?:name|date|age)' ,
'page' => '\d{1,5}' ,
))
* This source code was highlighted with Source Code Highlighter .
The same, only the delimiter is a comma. No limits for fantasy :)
Generally speaking about routs, I would recommend not to rely on one general rule, as in the old version, but to create more secure private descriptions. The truth is there is a small ambush associated with the fact that for each route a regular expression is generated when the application starts. This, of course, is not too fast with a couple dozen of routes, and therefore routes need to be cached:
if ( ! Route::cache())
{
Route:: set ( /*…*/ );
Route:: set ( /*…*/ );
Route::cache(TRUE);
}
* This source code was highlighted with Source Code Highlighter .
In addition to routing addresses, routes serve another important purpose: with the help of them, these same addresses can be generated. To do this, there is the
Route-> uri ($ params) method, to which the missing parameters are passed as an associative array and the finished uri is returned.
Part Two, HMVC
Many have already heard that the new Kohana is based on the HMVC paradigm, which differs from the usual MVC in that any of the components can launch another request to any controller for their needs, bypassing the http protocol. This is easy to demonstrate with an example:
/*
* id 12
*/
Request::factory( '/wigets/advert/client12' )
->execute()
* This source code was highlighted with Source Code Highlighter .
The factory method is passed the url string, which reruns the routing loop.
Actually the processing of the main request that comes in via http is not much different from the one shown above:
/**
* Execute the main request. A source of the URI can be passed, eg: $_SERVER['PATH_INFO'].
* If no source is specified, the URI will be automatically detected.
*/
echo Request::instance()
->execute()
->send_headers();
* This source code was highlighted with Source Code Highlighter .
The only difference is in how we create the request object, either by a singleton (the main request) or through a factory. In this and in another case, we get the answer in the property
Request-> response (this is the tautology with the names). If we try to convert the
Request object to a string (as echo does in the second example), it returns
$ this-> response .
From this difference in the method of obtaining the Request object, one strict rule follows when working with the third Kohana - you must clearly understand which
Request object should be contacted to obtain the request variables and return the result. The request of the entire application is always available by
Request :: instance () , and the request for the current route is available from
Controller-> request , i.e. in the controller $ this-> request.
In fact, the need for such an approach is long overdue. Any large site has in its design all kinds of widgets, sidebars and other blocks, the contents of which weakly depend on the main content on the page, but it requires the controller to work. Previously, it was necessary to be wise, if the unit was needed within the same controller, a separate method was made. If in a larger number of controllers, it was transferred to a model or a presentation, depending on the complexity of the block. Now, with this done, you can feel free to call any url in the submission directly like this:
<div class = "sidebar" >
<?= Request::factory( '/wigets/voting' )->execute() ?>
</div>
* This source code was highlighted with Source Code Highlighter .
And now an additional bonus: if we have some form with data sending (for example, voting) in this block, we can send ajax requests directly to / wigets / voting and receive the text of this block without any additional checks, ajax it was or not.
Everything related to content blocks is great, of course, but the use of HMVC in Kohane is not limited to this. The fact is that no one limits us in choosing the format of the returned data. We can write to the
Request-> response data of any type and absolutely easy to accept them on the other side. I still can not think of what this may be needed for, but the fact that any data can now be obtained in any controller or presentation is very pleasing.
Another feature that I noticed in the
Request work is that the
execute () method for a single object can be called as many times as needed. Moreover, the Kohana does not do any manipulations with the response property between requests, including cleansing. This is both a bug and a feature. First, be careful, reset the response yourself, and secondly, keep in mind that it is possible to accumulate data between requests (provided that the
Request object does not change, of course).
Part Three, better routing and HMVC integration
Well, now some reasoning from me personally. The proposed method for creating a Request :: factory ('url') object is no good. In fact, you should always do smarter:
Request::factory(Route:: get ( 'wigets' )->uri(array(
'action' => 'userslist' ,
'sorting' => 'name' ,
)));
* This source code was highlighted with Source Code Highlighter .
Those. do not manually compose the address, but generate it from the route.
And here, in my opinion, lies a small flaw in the architecture. The fact is that we pass parameters to the route to get the string uri, and we pass the string to Request to get the parameters. This is accompanied by a loss of performance when calling:
- losses on the generation of the string uri (it is not needed for the execution of the controller)
- extra checks for coincidence with routes (we know the route in advance)
- analysis of uri for parameters
It would be great, I thought, if I could do something like this:
Request::factory( 'wigets' , array(
'action' => 'userslist' ,
'sorting' => 'name' ,
));
* This source code was highlighted with Source Code Highlighter .
I thought, and wrote a module blocking method Request :: factory. The module complements the query creation interface as shown above. In addition, the execution time for empty queries is halved. The first parameter is the name of the route, the second must be an array of parameters. If the second parameter is not specified, the first, as before, is considered a string uri. Those. no functionality breaks.
Download module.The only side effect of using such a notation is that the
Request-> uri line will contain not the real uri specified in the route, but a line like / route / controller / action. In particular, this can be seen in the profiler output.
Thanks for attention.
A huge request to the experts of the Ukrainian language, refrain. Everything is already up to date .