sonic icon indicating copy to clipboard operation
sonic copied to clipboard

add 'fall-through' routine in router

Open csurf opened this issue 12 years ago • 5 comments

currently, a user is forced to manually configure each&every single router->action+params combination in order for sonic to successfully route a request. If a given route is not specified in the routes.php config file, it is completely ignored, even though the requested controller+method do exist. example: class main extends controller{ ... public function test(){echo 'test';} ... } navigating to '/main/test' fails if this specific route hasnt been explicitly defined in routes.php

It becomes tedious having to manually manage every single route, and becomes a possible point of failure/errors.

So, allow for a 'fallback test' that can load a route from a 'controller+method' combination, assuming that they exist. This can be a user-configurable option; the test can be skipped if the user doesn't want or need the fallback, in which case only the defined routes will be applied.

I thought perhaps this could be applied in Router.php, in the _getMatch() function, somewhere toward the end, after the routes have been processed and before the function 'falls through' to returning 'null' if no match was found.

as a test, I simply 'exploded' the uri string and returned the array, but the problem with this method is that it will try to call the 2nd item in the uri array as a method, and the 'transformation' class will 'trigger a fatal error' instead of 'throwing an exception' if the requested method doesnt exist. The exception is required in order to handle the non-existent, i.e. show the error view and/or return a 404, etc.

csurf avatar May 12 '13 03:05 csurf

So this is actually already somewhat supported, but I apologize for it not being documented.

You can set up wildcard routes using ACTION and CONTROLLER arguments which would look something like this:

$routes = array(
    '/special/:CONTROLLER/:ACTION' => array(),
    '/special/:ACTION' => array('special'),
    'r:/action/(one|two|three|dash-test)$' => array('action', 'not_found', array(1 => 'ACTION'))
);

This means that /special/test would route to special controller test method. special/foo/bar would route to foo controller bar method.

The third example is how you could use regex to verify that the action or controller matches a regex pattern rather than just blindly passing it on.

I don't think any sort of magic routes should be included by default though.

ccampbell avatar May 12 '13 04:05 ccampbell

ok, cool, I didnt dig deep enough into the routing logic to see those routing keywords. thanks for the info, will test & verify that it works. Should the issue stay open to serve as documentation? let me know. thanks again.

csurf avatar May 12 '13 04:05 csurf

Imstill having issues with this... example controller 'main' method 'test', with 2 optional 'float' args... class main...{ function test($arg1=1.5,$arg2=0){...}

/main/test/300/2.75 or optionally /main/test/10.5 or even just /main/test i couldnt seem to find a pattern that could work using the ':CONTROLLER' and ':ACTION' keywords. the _matches() function bombs if the the uri string & route string have different 'segment' counts, so Im not seeing how I could get this to work. I am basically looking for a 'default' mapping from uri string to (existing) controller/method+optional args...is this currently possible, or would the code need to be reworked to handle this?

csurf avatar May 12 '13 11:05 csurf

Arguments do not get passed in directly to the controller methods (although they probably should). To get them you have to do:

$routes = array(
    '/special/:ACTION/:arg1' => array('special')
);

And in the controller

public function test()
{
    // using input filter
    $arg1 = $this->filter('arg1')->setType('float')->getValue(Request::PARAM);

    // using getParam
    $arg1 = $this->getParam('arg1');
}

To get GET or POST valuables

$arg = $this->getPost('arg');
$get_arg = $this->getGet('arg');

// or
$arg = $this->filter('arg1')->setType('string')->getValue(Request::POST);

Although probably getParam() should merge GET and POST and router args.

ccampbell avatar May 12 '13 15:05 ccampbell

You can see some examples in the tests https://github.com/ccampbell/sonic/blob/master/tests/InputFilterTest.php https://github.com/ccampbell/sonic/blob/master/tests/RouterTest.php

ccampbell avatar May 12 '13 15:05 ccampbell