Understing URL routing in MVC - php

I am trying to create a very basic MVC framework to better understand the pattern.
I am having trouble understanding the URL routing part.
So far i've understood that the url carries 3 basic pieces of information in this format:
www.site.com/controller/method/querystring
So given the following URL:
www.site.com/user/delete/john
'user' is the controller
'delete' is the method of said controller
'john' is the query string
In my framework, i have it so if a controller is not specified in the URL, it defaults to 'index'.
If a method if not specified in the URL, it defaults to 'show'(which just outputs the html).
this way i can go to www.site.com and since it doesn't have a controller or method in the url, the
controller becomes 'index' and method 'show', thus just loading the index view.
But what if i don't want to provide a method in the url, but just www.site.com/controller/querystring
like so:
www.site.com/user/john
This would ideally load the profile for John.
But, the framework thinks 'john' in the url is the method to invoke, and not the query string.
What is the standard, a practical way to distinguish between the two?
ps:
I have this in my .htaccess
RewriteRule ^(.*)$ index.php?$1 [L,QSA]
echoing $_SERVER['QUERY_STRING'] in to http://site/profile/john gives 'profile/john'/

My controllers usually locate a handler method searching from more to less specific. If a method is found, it's called with the "tail" of parameters passed to it. For example, if user/delete/john is given, it attempts to call, in order:
action_user_delete_john()
action_user_delete('john')
action_user('delete/john')
generic_fallback_method('user/delete/john')
In your case, i'd define a set of user_<operation> methods (action_user_delete, action_user_edit etc) and a default action_user method which will be called when no operation param is provided and should handle urls like user/john
I find this technique quite flexible and powerful, but there's no standard, and you're free to invent your own.

Related

Make title string the entire URI for all pages using CodeIgniter

With CodeIgniter I'm trying to create a URL structure that uses a title string as the entire URI; so for example: www.example.com/this-is-a-title-string
I'm pretty confident I need to use the url_title() function in the URL Helper along with the routes.php config folder but I'm stuck bringing it all together.
Where do I define the URI and how is it caught by the routes folder?
Seems to be a straight forward problem but I'm getting stuck creating the URLs end-to-end. What am I missing?
I thought about a catch-all in the routes folder: $route['(.*)'] = "welcome/controller/$1"; ....but how would this work with multiple functions inside a particular controller? ...and maybe it's not even the right way to solve.
You can send all requests to a driver with something like this:
$route['(:any)'] = "welcome/function";
Then use the _remap function to route requests inside the controller.
However, using URL's as you suggest limits the CI functionality. Try something better like www.example.com/article/this-is-a-title-string
$route['article/(:any)'] = "articles/index";
and in article (controller), use _remap...
If you're going to re-route every request, you should extend CI_Router.
The actual implementation depends on what you're doing. If you customize CI_Router, you can do it AFTER the code that checks routes.php, so that you can keep routes.php available for future customization.
If the URI contains the controller, function, and parameters, you can parse it within your extended CI_Router and then continue with the request like normal.
If the URI is arbitrary, then you'll need something (file, db, etc) that maps the URI to the correct controller/function/parameters. Using blog posts as an example, you can search for the URI (aka post-slug in WordPress) in the db and grab the corresponding record. Then forward the request to something like "articles/view/ID".

rewriting urls in codeigniter

How can I rewrite the following codeigniter url
localhost/test_project/pages/show_page/11
to
localhost/test_project/pages/11
and is it possible to further reduce like:
localhost/test_project/11
Thanks.
You should edit this file: application/config/routes.php.
Let's assume you have a pages controller and a show_page method with receives an id as a parameter (just as you said).
your URL is like localhost/test_project/pages/show_page/11
I assume that the parameter is always a number, so using wildcards as described here you can add the following to your routes.php file:
$route['pages/(:num)'] = "pages/show_page/$1";
So your URLs will be like localhost/test_project/pages/3. This is like saying if anyone tried to visit localhost/test_project/pages/3, he/she actually means localhost/test_project/pages/show_page/3.
For urls like localhost/test_project/3 you can add this:
$route['(:num)'] = "pages/show_page/$1";
You've a few options. These are in descending order of priority.
First there is routes.php, which is the generally preferred way of handling things (information already provided, but here it is again).
You can also add a _resolve method to a controller (which means that CI needs to get to your controller to begin with), and
finally you can override the CI_Router library, the class which actually returns the route to go to.
Seriously, don't override CI_Router unless you know what you're doing.

Remapping Codeigniter Controller

I've been playing with Codeigniter lately, and I came to know that you can remap a function inside a Controller so that you can have dynamic pretty URL.
I just want to know can the same be done with controllers? I mean If I call http://example.com/stack, it'll look for a controller named stack and if not found, it'll call a fixed/remapped controller where I can take care of it.
Can this be done?
Maybe this can help you:
function _remap( $method )
{
/// $method contains the second segment of your URI
switch( $method )
{
case 'about-me':
$this->about_me();
break;
case 'successful':
$this->display_successful_message();
break;
default:
$this->page_not_found();
break;
}
}
Yes, it can be done, you achieve it by using uri routing.
In your application/config/routes.php you can set your custom routes to remapping URIs.
There are already 2 provided, the default one (in case no controller is called) and the 404 error routing.
Now, if you want to add custom routes, you just add them UNDER those 2 defaults, keeping in mind that they're executed in the order they're presented.
Say, for example, you want to remap 'stack' to another controller, just use:
$routes['stack'] = 'othercontroller';
In this way, whenever you access 'stack' it will be automatically mapped to 'othercontroller', and if that doesn't exists..well, you get the same 404 error.
If you're hiding index.php from the URL with .htaccess remember to insert it into the $config['index_page'] = 'index.php';.
If what you're trying to achieve, instead, is a custom error message when a controller is not found, just override the 404 route as already suggested by #Juris Malinens, using your custom default controller to handle that situation
$route['404_override'] = 'customcontroller';
You can use .htaccess to do this or config/routes.php- Codeigniter is very flexible ;-)
If controller is not found use $route['404_override']; in config/routes.php
The application/config/routes.php file would be the appropriate place to do this. The 404_override mentioned by Juris is only available in CI 2.x just in case you have an older version (I don't know, you may be working on a legacy system, or may have to in the future).
Note, you can do more than just "remap" controllers with this. The routes accept regex patterns like htaccess rewrite rules; there are also some CI patterns which are basically just more human readable alias for regexes. Say you had an Articles controller with category, search and article functions, you might have routes that looked like:
$route["category/(:any)"] = "articles/category/$1";
$route["search/(:any)"] = "articles/search/$1";
$route["(:any)"] = "articles/article/$1';
You see how you can use routes to completely remove the controller name from you URLs? These rules would fall back to assuming the page is an article if the URL doesn't specifically say it's a category page or a search query. You could then check if you had an article for the URL and display a 404 as appropriate.

MVC - No Controller in the URI?

Let's pretend I'm trying to learn CI, and as my test project I am building a group-buying site.
What I'd like is to have a different page for each city, e.g.:
http://www.groupon.com/las-vegas/
http://www.groupon.com/orlando/
I'd also like to have different pages such as:
http://www.groupon.com/learn
http://www.groupon.com/contact-us
If I am building this in CI and following the MVC ideology, how would this work? I'm having difficulty seeing how to accomplish the desired URL's with the concept of:
http://www.domain.com/controller/view/segment_a/segment_b/etc...
What I would do is create a custom 404 controller that acts as a catch-all for non-existent routes.
It would take the URI, possibly validate it, and re-route it to the (e.g.) "city" controller.
If the city controller can't find the city (whatever string was specified), then it needs to issue a proper 404. Otherwise, you're good to display your information for that city.
Also, once you create your custom 404 controller, you can send all 404 errors to it by specifying a route named '404_override'.
That's where URI Routing comes in. But in your case you'll probably will have to be carefull defining your routes as the first and only part of your route is a variable part already.
This really has nothing to do with MVC, and much more to do with good URL.
You're looking for URLs that are both (a) clear from the user's point of view and (b) that give hints to your application as to how it's meant to be handled.
What I'd do in this case is redesign your URLs slightly so that rather than:
http://www.groupon.com/las-vegas/
http://www.groupon.com/orlando/
You would have URLs that looks like this:
http://www.groupon.com/destinations/las-vegas/
http://www.groupon.com/destinations/orlando/
The bit at the beginning--/destinations/--can be used by your URL routing code to decide what controller should be dealing with it. If your routing code is URL-based, you might have an array like this:
$routes = array(
'/destinations/' => 'on_destination_list',
'/destinations/(.+)' => 'on_destination',
'/(.*)' => 'on_page');
// Basic URI routing code based off of REQUEST_URI
foreach ($pattern => $func) {
if (preg_match("`^$pattern$`", $_SERVER['REQUEST_URI'], $placeholders)) {
array_shift($placeholders);
call_user_func($func, $placeholders);
}
}
Keep in mind that I wrote that routing code off the top of my head and it may not be absolutely correct. It should give you the gist of what you need to do.
Doing things this way has the added benefit that if somebody goes to http://www.groupon.com/destinations/, you'll have the opportunity to show a list of destinations.

Are PHP MVC Controllers implemented by url rewrite?

The controller in a Java EE
application may be represented by a
servlet, which may be currently
implemented using JavaServer Faces
(JSF).
But in PHP there is no such servlet,so I guess is it implemented by url rewrite?
So that every request is directed to that controller?
It can be done with mod_rewrite but mostly in php there is a front-controller mechanisim which does all controlling through a single file. In this way, all controllers are specified in the url. See this for more explanation about controllers and mvc in php.
I think that's called the Front Controller pattern http://en.wikipedia.org/wiki/Front_controller and usually is achieved via mod_rewrite rules that any requests for phisically nonexisting files is redirected to index.php which then decides what to do.
MVC in PHP typically makes use of a front controller, which serves as the only entry point into the application.
This is usually implemented by using mod_rewrite to point all requests to a php file containing your bootstrap code.
This bootstrap code will contain your front controller.
The front controller takes it from there, redirecting the request based on the input parameters to the appropriate controller. Your target controller is usually specified as one of the parameters.
So, when you hit:
http://yourdomain.com/blog/new
The application would redirect the request to your bootstrap code with your front controller, which will interpret this input to mean 'blog' is the requested controller, 'new' is the action. It would then instantiate the 'blog' controller, pass it the 'new' action, and the rest is standard MVC.
As most of the other answers have shown, usually mod_rewrite is the way to do it. But, if you don't want to use mod_rewrite, you can let your bootstrap file actually configure variables from the URL itself.
I use a couple of my own functions which creates an array from a URL, so:
site.com/page/welcome/param1/param2
becomes
$url[0] = 'page'
$url[1] = 'welcome'
$url[2] = 'param1'
$url[3] = 'param2'
and then I can pass the $url array to my Router and it decides which parts of the Controller/Action/Param call each element belongs to.
The same URL request shown above creates the Controller/Action/Param call:
// http request for site.com/page/welcome/param1/param2
$controller = new Page_Controller('param1', 'param2');
$controller->welcomeAction();
while, depending on the settings in my Router object, I can create subdirectories, such as for calls to 'admin/':
// http request for site.com/admin/page/welcome/param1/param2
$controller = new Admin_Page_Controller('param1', 'param2');
$controller->welcomeAction();
With url_rewrite I think it would be harder (still possible though) to add those reroutes in, and with my method (Some frameworks might also do it, not too sure) it allows you to customize it more, ie you can edit the $url array if needed before passing it to the Router object.
I'm not sure what the negatives are in using this method, but it works pretty well for me!
You would use mod_rewrite to redirect everything to the index.php file. So you use mod_rewrite if you want this:
http://example.com/page/welcome
and not
http://example.com/index.php?page/welcome
or
http://example.com/index.php?controller=page&action=welcome

Categories