Page 1 of 2
A very very basic routing class.
Posted: Fri Mar 29, 2013 11:07 am
by Chris30
I want this feature for my framework.
Route::get('/account/(int)', function() {
Forward('account, array($1))
}
So in reality; ?page=account&memberID=5 would become; /account/5
I would like to see a very small example of how it is done. Manually get URI, explode it by / symbols, handle it accordingly?
I want to see a very basic example of this task. Checked the routing classes in frameworks on github but they are pretty huge. I don't need that. I just want a singlepage class if possible, so I can create mine by referencing to it.
Thanks!
Re: A very very basic routing class.
Posted: Fri Mar 29, 2013 11:10 pm
by requinix
Having done this kind of thing a couple times before, might I suggest this?
Code: Select all
Route::add('/account/(\d+)', function($dispatch, $matches) {
$dispatch->Forward('account', array($matches[1]));
});
1. The first argument is a regular expression you run through preg_match()
Code: Select all
// example data
$url = '/account/5';
$test = '/account/(\d+)';
// here's how you use them
if (preg_match("#^{$test}$#", $url, $matches)) {
// matched. now send $matches to the callback function
} else {
// did not match. try something else
}
2. $dispatch and $matches are so that the callback has access to the things it needs: namely the object that it calls ->Forward() on and the matched data from the regex it gave
---
But you can go even further.
Code: Select all
Route::add('/account/(?P<memberID>\d+)', 'account');
with the default action of the "forward" thing - assuming, of course, that there are other things it could have done. Then 'account' (whatever that is?) gets the array of matches from the regex but now the ID in there is named "memberID". It does require you to know a little bit more about regular expressions but it saves you from writing a lot of interstitial code.
Re: A very very basic routing class.
Posted: Sat Mar 30, 2013 3:05 am
by Chris30
How do we get the $url and $test values, $_SERVER['REQUEST_URI']? Also, does the URI depends on platform (like directory differences (/ and \\) between unix and win) or always returns the correct URI?
Re: A very very basic routing class.
Posted: Sat Mar 30, 2013 6:09 pm
by requinix
Look in $_SERVER. You can use the REQUEST_URI but that will also have the query string. And no, the URI will always use forward slashes.
Re: A very very basic routing class.
Posted: Mon Apr 01, 2013 12:23 pm
by Chris30
I made something like this.
Code: Select all
//in Route class
function get($uri)
{
if($uri == '/account')
{
return true;
}
else
{
return false;
}
}
function Boo()
{
echo "Boo";
}
//in index.php
$route = new Route();
$route->get('/account', function() use ($route) {
return $route->Boo();
});
It does not call $route->Boo().
A guy on stackoverflow said I should pass a second parameter to get function, (e.g $uri, Closure $closure) but it will be pretty bad looking.
I want to be able to write PHP codes inside my closure, like;
Code: Select all
$route->get('/account', function() use ($route) {
//Do something what you can do without closure, like loading a view file (e.g $view->create('account.tpl');
});
How can I do this?
Re: A very very basic routing class.
Posted: Mon Apr 01, 2013 1:01 pm
by requinix
Of course it doesn't call that method: all get() does is return true or false. Seriously. Read the code.
Try more like
Code: Select all
//in Route class
function get($uri, $callback)
{
if($uri == '/account')
{
$callback();
return true;
}
else
{
return false;
}
}
Re: A very very basic routing class.
Posted: Mon Apr 01, 2013 5:08 pm
by Chris30
Okay but, handling callback inside the get function isn't something I prefer doing. The route class doesn't connect other classes.
For example, if there is a wrong URI given, I would like to print load 404 view, hence use the template model.
index.php
Code: Select all
if($route->get('account') == true)
$controller->Run();
else
$template->loadView('404.tpl');
Index.php has access to $template, but Route class is not. (and should not, it is not his purpose to handle template)
For that reason, I want to handle callback in a different scope. Like the scope in index.php itself so I can use $template->loadView('404.tpl'); inside the closure.
Is that not possible with PHP?
Re: A very very basic routing class.
Posted: Mon Apr 01, 2013 5:55 pm
by requinix
I'm sure it is possible, whatever you're trying to say. What's not possible is for the script to behave in a certain way when you specifically do not want to write the code to allow it to behave in the certain way.
How about this angle. Inside index.php what kind of code do you want to write? What should it look like?
Re: A very very basic routing class.
Posted: Tue Apr 02, 2013 6:26 pm
by Chris30
I don't think I can explain myself any clearer than this.
Let's have a look at your example.
Code: Select all
//in Route class
function get($uri, $callback)
{
if($uri == '/account')
{
$callback();
return true;
}
else
{
return false;
}
}
We defined a second paramter to get() function to handle callbacks. However;
1. I don't want to define a secondary parameter in every function to handle callbacks. I want to handle them somewhere else. Not inside a function inside a basic routing class.
2. The routing class has no access to other controllers. So get() function cannot take "$this->database->mysql->query" as a callback because Route class has no access to database object. It is out of it's scope.
In short, I want to do something like this:
Code: Select all
index.php
$route->get('/account', function() use ($route) //Like in javascript, get('/account') returns true so we can do something inside the closure.
{
$this->database->mysql->query('BOOM BOOM <span style='color:blue' title='I'm naughty, are you naughty?'>smurf</span>');
or
return Template::load('error.tpl');
});
But without as a secondary parameter to get function. I may make some other functions than get and I have to re-define a callback eachtime.
How else, without using a second parameter named "Closure $closure" in get() function, you can solve the code above?
Re: A very very basic routing class.
Posted: Tue Apr 02, 2013 7:18 pm
by requinix
1. Then don't use callbacks.
2. I don't know why you're saying it needs to pass mysql->query as a callback. That's not what's happening at all.
As for your sample code, unless you're on 5.4+ you can't use $this directly inside a closure. The Template stuff will work. So is that a solution? I still can't tell what the problem is besides you not wanting to pass along a "dispatcher" object from the Route class (something which, if it exists, the router would most certainly have available) to the callback, and that you've changed the desired behavior of this code a few times in this thread.
So as for my earlier question of what you want your index.php code to look like, is
Code: Select all
$route->get('/account', function() use ($route)
{
return Template::load('error.tpl');
});
this it? Where is the result of that ::load() going? You've already said the router has no access to dispatchers, controllers, or databases.
FYI in this pattern, which it seems like you're trying very hard to get right, the router class is in charge of figuring out where a request should be handled. That's all. It shouldn't actually be executing the actions - that's the job of a front controller or dispatcher.
Re: A very very basic routing class.
Posted: Thu Apr 04, 2013 4:14 pm
by Chris30
Well. How does Laravel handle it then?
http://laravel.com/docs/routing
Code: Select all
Route::get('/', function()
{
return "Hello World!";
});
or
Code: Select all
Event::listen('404', function()
{
return Response::error('404');
});
Route class only looks for an $action parameter. (In sources of Laravel Route class)
Re: A very very basic routing class.
Posted: Thu Apr 04, 2013 4:48 pm
by requinix
Because that there encapsulates routing and controllers. Those functions are the controllers.
Re: A very very basic routing class.
Posted: Thu Apr 04, 2013 5:12 pm
by Christopher
You could do something like this:
Code: Select all
class Route
{
protected static $routes = array();
public static function get($route, $function)
{
self::$routes[$route] = $function;
}
public static function run($route)
{
if (isset(self::$routes[$route]) && is_callable(self::$routes[$route])) {
call_user_func(self::$routes[$route]);
}
}
}
Route::get('/', function()
{
echo "Hello World!";
});
Route::run('/');
Re: A very very basic routing class.
Posted: Thu Apr 04, 2013 6:15 pm
by Chris30
Christopher wrote:You could do something like this:
Code: Select all
class Route
{
protected static $routes = array();
public static function get($route, $function)
{
self::$routes[$route] = $function;
}
public static function run($route)
{
if (isset(self::$routes[$route]) && is_callable(self::$routes[$route])) {
call_user_func(self::$routes[$route]);
}
}
}
Route::get('/', function()
{
echo "Hello World!";
});
Route::run('/');
Yeah, things are alot clearer now! Laravel also does something like this, right?
Re: A very very basic routing class.
Posted: Thu Apr 04, 2013 10:03 pm
by Christopher
Chris30 wrote:Laravel also does something like this, right?
I haven't looked at the code, so don't know. This is the style of microframeworks. They are for sites that don't have a lot of pages and the pages are fairly simple. For larger sites you want to use Action Controller classes that are loaded on request.