SitePoint Sponsor |
|
User Tag List
Results 26 to 50 of 51
Thread: MVC Router Implementations
-
Mar 16, 2009, 06:29 #26
- Join Date
- Jun 2004
- Location
- Copenhagen, Denmark
- Posts
- 6,157
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
I'm a little late to this party, so excuse me if I reiterate some points, but since my name came up I thought I'd just add a few clarifications.
One of the things that works well with hierarchical controllers, is that it forms a coherent presentational unit. Amongst other things, this makes for a practical way of managing urls and url-propagated state in a transparent way to the rest of the application. It's a different way of cutting the presentation layer, than frontcontroller + mvc. Controllers (or components, as they are called in Konstrukt) tend to be smaller and control only part of the rendering of the page, while having a tighter coupling between controller and view layer. This means that you can't as easily change the view of your application, without touching the controller layer, but the benefit is that you gain a much tighter control of the http-level interaction. In many types of applications, I find that trade worthwhile, since it makes it easier to create a good end-to-end user interaction. It also does away with some (in some cases) redundant abstractions.
With hierarchical controllers, you can spread the handling out over multiple controllers. In a Konstrukt application, you would usually have one "final" handler, which corresponds to the "action" of Rails. However, you would often have several parent handlers, which add a bit of rendering (Navigation for example) or provide contextual model level access for the final handler. This roughly corresponds to Rails "layouts", except that you're not restricted to one, and it isn't just for rendering the view.
Since components are completely contextual, it is quite easy to move them around. URLs are generated by refering to the parent component, so when a component is moved the urls will automatically change. In fact, you could use the same component in multiple places in your application at the same time. This is especially useful if you want to combine different applications, that weren't written for each other, because they won't fight over the same global resource (the route). In general, it's a more flexible and modular design; The problems with it is rather that it is slightly more abstracted. As a developer, you can't go to a single place and get an overview of all possible urls in the application.
-
Mar 16, 2009, 07:58 #27
- Join Date
- Jun 2004
- Location
- Netherlands
- Posts
- 172
- Mentioned
- 2 Post(s)
- Tagged
- 0 Thread(s)
It's an interesting approach and different from what you see in most frameworks like Zends. I guess there are certain advantages and disadvantages (as always) and which is more important depends on the kind of webapp you build
One thing I do wonder about, if each parent handler is responsible for a certain part of the rendering (the layout with header and navigation for example), how do you feed back information from a child to its parent? For example the child controller will have information about the "current page or section", information that is needed in the parent to display in the navigation menu ("the currently selected menu item")
-
Mar 16, 2009, 14:02 #28
- Join Date
- Jun 2004
- Location
- Copenhagen, Denmark
- Posts
- 6,157
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Certainly. I'd say it shines most with a fairly complex, hierarchical interface. That generally means guis for applications, rather than web sites.
A component knows the "remainder" part of the url (Called subspace), so you'd usually render the navigation in the component immediately parent to the final handler. I've build some applications with a gui that consisted of layered "tabs", where each component in the chain rendered a wrapping around each other. Works really well, because the graphical presentation mimics the logical structure of the application, making it easy to navigate in.
-
Mar 16, 2009, 15:51 #29
- Join Date
- Jun 2004
- Location
- Netherlands
- Posts
- 172
- Mentioned
- 2 Post(s)
- Tagged
- 0 Thread(s)
Ok, sounds interesting. I have looked at Konstrukt in the past, maybe I should do it again and give it a better look.
-
Mar 17, 2009, 01:49 #30
- Join Date
- Jun 2004
- Location
- Netherlands
- Posts
- 172
- Mentioned
- 2 Post(s)
- Tagged
- 0 Thread(s)
Correct me if I'm wrong, but as I understand it now, in Konstrukt each and every URL variable maps to a single controller. So there is no way a single controller handles more parameters?
So an URL like
http://www.site.com/hotels/in/englan.../2009/guests/2
would mean I need to write 10 controllers?
-
Mar 17, 2009, 02:50 #31
- Join Date
- Feb 2006
- Location
- Netherlands
- Posts
- 295
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Yes, I blog, too.
-
Mar 17, 2009, 03:26 #32
- Join Date
- Mar 2006
- Location
- Sweden
- Posts
- 451
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Not exactly. The default behaviour is that each segment is handled by it's own component, but you can override that so that basically only one component handles the whole chain. But as webaddictz wrote, the last segments there should really be query strings and not segments.
I understand that it looks pretty to have a url like:
http://www.site.com/hotels/in/englan.../2009/guests/2
But what's the difference between this url:
http://www.site.com/hotels/in/
and:
http://www.site.com/hotels/
?
-
Mar 17, 2009, 05:37 #33
- Join Date
- Jun 2004
- Location
- Netherlands
- Posts
- 172
- Mentioned
- 2 Post(s)
- Tagged
- 0 Thread(s)
Well that URL is just an example, but I can think of more URLs with multiple arguments which could be defined as "query strings", but at the same time look better as segments.
Just think of the average weblog with
/category/php/2007/10/08/cool-routing
Those arguments php, 2007, 10, 08 could all be defined as query string. But at the same time they could be a resource as well so
/category/php/2007/10/shows all posts in category php from 2007 / 10
I think I would prefer to write a single controller for handling archives, which would take the arguments for category, year, month, day and postname (or something like that) over having 6 controllers for each segment. Of course I am not familiar enough with Konstrukt to know how it works. But having 6 controllers each making a call to the "posts" model instead of having one controller making a call to the same model but then with different arguments doesn't sound too attractive to me.
I do understand that the way Konstrukt handles things forces one to rethink and sometimes simplify the URLs. But I can also see situations in which you just want a different, more flexible and free URL scheme
-
Mar 17, 2009, 08:12 #34
- Join Date
- Mar 2006
- Location
- Sweden
- Posts
- 451
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
The default approach in Konstrukt is not to let all components call your model layer, as that would be terribly inefficient. You'd usually only let the final component call the model layer, as it knows that it's the final component. Or, you'd let a higher level component call the model layer, and the lower level components queries the higher level component for those objects.
I absolutly agree that "/category/php/2007/10/08/" is a good url. Let's look at the differences of having one controller to handle all of the possible situations with that url, and having multiple.
If you only have one, you'd have to have some conditional logic to check if the url is /category/php/2007/, or /category/php/2007/10/, or /category/php/2007/10/08/, as those three would require different views because they present different things.
If you use three different components for this, you can place common logic in a higher level component, and let the Month component worry about months, etc.
Besides, there's no requirement that each segment is handled by a unique component. It could be three objects from the same class, with some conditional logic inside to check for date parts.
But I can also see situations in which you just want a different, more flexible and free URL scheme
-
Mar 17, 2009, 08:16 #35
- Join Date
- Feb 2006
- Location
- Netherlands
- Posts
- 295
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Yes, I blog, too.
-
Mar 17, 2009, 12:19 #36
- Join Date
- Jun 2004
- Location
- Netherlands
- Posts
- 172
- Mentioned
- 2 Post(s)
- Tagged
- 0 Thread(s)
In this case I don't think there's any conditional logic needed or more then one view. You have one method in one controller which handles those parameters (probably as a bunch of "where X=Y" calls to the model layer)
Well maybe I don't understand Konstrukt's ideas well enough. I've read the two tutorials on Konstrukt's site but yet fail to understand the philosophy/why behind the way it is set up (not saying its good/bad, just that I don't understand it yet)
-
Mar 17, 2009, 12:46 #37
- Join Date
- May 2006
- Location
- Lancaster University, UK
- Posts
- 7,062
- Mentioned
- 2 Post(s)
- Tagged
- 0 Thread(s)
What it needs is a good ol' Video tutorial.
CoderMaya did that with his framework and everyone LOVED it - because they understood it straight away.
If Konstrukt had a video tutorial, I think it would help alot of people understand it better.Jake Arkinstall
"Sometimes you don't need to reinvent the wheel;
Sometimes its enough to make that wheel more rounded"-Molona
-
Mar 18, 2009, 01:05 #38
- Join Date
- Mar 2006
- Location
- Sweden
- Posts
- 451
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
If the url only has a year, would you display that the same way as if the url had a year, month and day? That could be a very long list. And you'd probably want to write out the months as headlines above that months entries, which you wouldn't if the url contained a full date.
I know that the concept behind Konstrukt isn't always that easy to understand (I know, because I was a user of Konstrukt before I was a contributor). But when I understood how it all worked, I basically never wanted to do it any other way.
-
Mar 18, 2009, 02:29 #39
- Join Date
- Feb 2006
- Location
- Netherlands
- Posts
- 295
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
The thing that has helped me a lot when learning Konstrukt's way of work is the reference to REST. When I started looking up articles on REST it hit me: every URL is a Resource and in Konstrukt, every Resource is a Component. It's really simple once you've got the hang of it.
Yes, I blog, too.
-
Mar 18, 2009, 06:47 #40
- Join Date
- Jul 2005
- Location
- Norway
- Posts
- 88
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
I have been thinking about a different approach to mvc for web lately. Don't know if this idea is good or bad. I'll lay it out and then you can judge for yourselves.
Key point:
- When routing everything to one file you have to mess with the normal flow of a webapplication. You add a front-controller which with the help of a router implementation figure out which controllers to load. One also oftentimes add a .htaccess file for creating pretty urls. In other words, you add complexity. Is it neccessary?
Alternate approach:
- Folders determine structure (categories, modules etc)
- When calling a controller action, one calls a file directly (/app/articles/edit.php) and in that file is the logic for that controller action
- Each controller-file include optional parent files such as configuration, db-connection, template, authorization and module-specific pre-dispatch logic. These are usually grouped together into a single include-file which is included at the top of each controller-file
- For each module in your app, you can create a different include file for your controller actions, so that you can have module-specific logic passed on to each controller-action in your module
- Use autoloading for library-classes and model-classes
A typical controller file:
PHP Code:require 'core.inc.php';
$form = new Form_EditArticle();
if (!Request::isPost()) {
$form->populate(Model_Articles::get(Request::get('id')));
} else {
if ($form->isValid(Request::post())) {
Model_Articles::add($form->getValues());
Response::redirect('index.php');
}
}
require VIEW_PATH . 'articles/edit.php';
PHP Code:require TEMPLATE_PATH . 'cms.php';
head('Edit article');
echo $form;
foot();
PHP Code://Define path to view files
define('VIEW_PATH', '/usr/app/views/');
//Define path to template files
define('TEMPLATE_PATH', '/usr/app/templates/');
//Include autoloader for the library
require realpath(dirname(__FILE__) . '/../../lib/autoloader.php');
//Autoloader for forms
//...
//Autoloader for models
//...
//Authentication
//....
//Db connection etc...
- No need to mess with urls and translate them into something meaningful
- Easy to see what's going on, and how things fit together.
- No need for .htaccess
- Unlimited categories/modules/subcategories
- Easy to create actions/pages that needs to be different (like ajax responses, csv responses, upload script for images etc..)
- Easy to move modules around.
Cons:
- Not suitable for cases where you have dynamic urls (like a website that get its content and structure from a database)
- If you really don't want to have .php in your url.
-
Mar 18, 2009, 07:21 #41
- Join Date
- Jun 2004
- Location
- Copenhagen, Denmark
- Posts
- 6,157
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
You can save /app/articles/edit.php as /app/articles/edit/index.php to get around that problem.
What you're describing is basically what Fowler calls a page controller (As opposed to a front controller). It has its merits, but it certainly has some limitations as you mention. The biggest benefit of it today, as far as I see it, is that it's the default modus for PHP, which means that you can expect almost anyone with prior PHP experience to understand it. That could have some value, especially in open source projects. And actually, that still seems to be the default for the popular open source PHP applications out there.
-
Mar 18, 2009, 18:37 #42
- Join Date
- Dec 2003
- Location
- Albany, New York
- Posts
- 1,355
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
I've done my fair share of this, and it definitely has its advantages and disadvantages. The simplicity is very nice, and it makes starting a new project take almost no time. Likewise, it makes for code that is very easy to follow.
You do lose something by not piping all of your requests through one file, though. Manually including core.inc.php and your view files gets old quickly, for example. You also can't catch any uncaught exceptions in a central place and give an error message. Instead, your script will stop executing and either display an error or go silent (depending on display_errors, of course).
There are really quite a few pros and cons to every setup. I've got to say, though, I'm starting to like this hierarchical idea.
-
Mar 19, 2009, 07:22 #43
- Join Date
- Feb 2004
- Location
- Montreal
- Posts
- 77
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
When I started a recent project, I wanted to get things up and running as quickly as possible and so this was almost exactly the approach that I took. However, instead of putting the controller stuff in the global level, I put action handler functions in the page controller file. The global include file ('core.inc.php' in your case) would determine which action function to call from the request uri and would look throughout the module path for a 'module.init.php' file that it would load as well as a similar, but global 'site.init.php'. This has worked pretty well so far, however, the major drawback has been the difficulty of internal redirects.
Regardless, for smaller-scoped projects, I've found the approach to be very simple for rapid prototyping and even development.
For the type of links discussed above, however, you will find that the page-controller approach is unrealistic. For example /blog/2009/03/ would necessitate a controller for each month of each year, all identical. Perhaps a simple .htaccess rule in the /blog/ subdirectory would suffice to remap the year and month as GET arguments. Anyway, I encourage you to try it out. At the minimum you will start with something that gets the job done and each iteration of functionality and refactoring that you add will improve your product. This is in contrast to trying to engineer everything in advance and never even starting the project itself (my problem).
-
Mar 19, 2009, 09:15 #44
- Join Date
- Jul 2005
- Location
- Norway
- Posts
- 88
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
You also can't catch any uncaught exceptions in a central place and give an error message
-
Mar 19, 2009, 09:46 #45
- Join Date
- Dec 2003
- Location
- Albany, New York
- Posts
- 1,355
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Fair enough. I've always preferred catching exceptions yourself to letting them go uncaught to an exception handler, but you're absolutely right.
I guess this is one of the issues where I prefer the standard MVC/FrontController method. It strikes me as somehow cleaner, though I'll admit that may just be personal preference.
There are limitations that aren't personal preferences, though. I promise.
-
Mar 19, 2009, 11:21 #46
- Join Date
- May 2008
- Location
- Montreal
- Posts
- 155
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
I implement routes in a similar way to the majority of frameworks out there. I store all defined specific/generic/remapped routes in an object and use that same object to try to match the current URI against one of those routes. Here are some examples of how routes are stored:
PHP Code:<?
$routes['/about'] = '/index/about';
$routes['/(:year)/(:month)'] = '/archive/index/$1/$2';
$routes['/(:year)/(:month)/(:day)'] = '/archive/index/$1/$2/$3';
$routes['/(:year)/(:month)/(:day)/([a-zA-Z0-9-]*)'] = '/view/post/$4';
[/ the deepest directory found][/ file name][/ extra / variables /...]
-
Mar 19, 2009, 14:02 #47
- Join Date
- Mar 2007
- Location
- Czech Republic
- Posts
- 375
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
I wonder what is better for route testing - regexps or successive checks of individual parts?
-
Mar 19, 2009, 14:17 #48
- Join Date
- May 2008
- Location
- Montreal
- Posts
- 155
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Mastodont, I use both approaches.
Each route that is added is split into two parts: everything up to, but excluding, the first generic part, for example: ( : year), and then everything that follows the start of the generic parts. This is the longest prefix of the route, and allows multiple routes of a common prefix to be grouped together (note: all routes without prefixes are also grouped together). Within these prefix groups, the variable part of the routes are sorted in descending order of their length, so that we try to match the remainder of the route against the longest regular expressions we can.
Given a URI, U, split U into a queue, Q, of non-empty parts by the regular expression ~\s*/+\s*~. Let S be an empty string, representing our current longest prefix found so far.
Code:While (Q is not empty) and (S+first(Q) exists as a longest prefix): S := S+shift(Q).
-
Mar 19, 2009, 14:36 #49
- Join Date
- Mar 2007
- Location
- Czech Republic
- Posts
- 375
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
How do you solve possible issue with different actions for various controllers? I.e. route
:controller/:action/...
I am trying now latter approach, split all routes and given URL, then subsequently eliminate routes which do not match.
-
Mar 19, 2009, 14:41 #50
- Join Date
- May 2008
- Location
- Montreal
- Posts
- 155
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Mastodont, this is either not solved using route re-mappings (i.e. the route parser just figures out the deepest that it can go to get a controller file, and accepts it), or it is solved using either generic or specific route re-mappings.
For the most part, the route re-mappings have the following uses:
- Limiting the number of arguments that get passed to the actions
- Making an action appear to be a controller
- Dealing with very variable routes, especially ones with common prefixes.
Should it interest you, you may find the code to the route parser here: http://ioreader.com/code/php/route-parser.phps
Bookmarks