ccampbell / sonic

fast, lightweight PHP 5.3 MVC framework
http://www.sonicframework.com
Apache License 2.0
63 stars 11 forks source link

add 'fall-through' routine in router #28

Open csurf opened 11 years ago

csurf commented 11 years ago

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.

ccampbell commented 11 years ago

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.

csurf commented 11 years ago

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 commented 11 years ago

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?

ccampbell commented 11 years ago

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 commented 11 years ago

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