Load url from routes for a controller - php

As CodeIgniter comes out of the box, it supportes one controller for each unique url.
So, let's say i have the following urls in my routes.php file:
$route['default_controller'] = "homepage";
$route['404_override'] = "homepage/not_found";
$route['^products$'] = "product/list";
$route['^product/(:any)$'] = "product/details";
My urls will look like this:
product list => http://www.mywebsite.com/products
product details => http://www.mywebsite.com/product/my-custom-product
In the controller where I build the products list, I create the url for each product like this:
$this->db->from('products');
$products_result = $this->db->get();
$data['products'] = array();
foreach ($products_result->result() as $row)
{
$data['products'][] = array(
'title' => $row->title,
'image' => $row->image,
'url' => site_url('product/' . $row->url)
);
}
$this->load->view('products/list_view', $data);
But there is too much redundancy for each url. I have to write the url every time I want to echo it somewhere. If I want to change the url, I have to open each php file and replace them all.
Question: Isn't there a "method" I can use to call the controller name and its method and that "method" returns the url for that one? Something like this:
build_site_url('product/details', array('my-product-url'));
where the first argument is the controller and its method (since only one controller exists for a url pattern), and the second argument is the array of url parts.

What you're looking for is a feature called reverse routing. There is no such feature built into CodeIgniter at this time, and any library I could find was done in like 2010, so probably not the most up-to-date.
However, there are articles and pull requests out there relating to reverse routing, so if you are experienced enough, you should be able to put something together for your application.
Otherwise, your best bet may be to create helpers for your most common URLs (like your products). So you could do something like echo product_url('my-product-name');, and you would only need to adjust the URL in the helper function.

I am not sure what exactly that you desired, but you can override controller behaviour using _remap() function;
look: http://ellislab.com/codeigniter/user-guide/general/controllers.html#remapping
e.g.
public function _remap($method){
if ($method == 'product/details') {
///do what you want to do
}

You could either use a default lookup function in the product controller (described in the CI Routing docs; look at the examples).
$route['product/(:any)'] = "catalog/product_lookup";
A URL with "product" as the first segment, and anything in the second
will be remapped to the "catalog" class and the "product_lookup"
method.
Or you could use database driven routes.
Add the table product_slugs to your MySQL database:
CREATE TABLE IF NOT EXISTS `product_slugs` (
`id` bigint(20) NOT NULL auto_increment,
`slug` varchar(192) collate utf8_unicode_ci NOT NULL
PRIMARY KEY (`id`),
KEY `slug` (`slug`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;
Replace the code in application/config/routes.php with the one below:
$route[ 'default_controller' ] = 'main';
$route[ '404_override' ] = 'error404';
require_once( BASEPATH .'database/DB'. EXT );
$db =& DB();
$query = $db->get( 'product_slugs' );
$result = $query->result();
foreach( $result as $row )
{
$route[ $row->slug ] = 'product/detail/$1;
}
All you would have to do then is to create a record when you create a product entry and you're done:
INSERT INTO `product_slugs` (`slug`) VALUES ('name-of-the-product');

Related

database driven codeigniter navbar

I am new with CodeIgniter and have been told that it is the way to go.
I have read the user manual a few times now but don't seem to understand one thing which is I wanted to do a navbar driven from the database. I found a few posts on here saying about a method that I have implemented but no matter what I do, it just doesn't seem to work for me. what I have done so far:
Created a table with :
CREATE TABLE IF NOT EXISTS `app_routes` (
`id` bigint(20) NOT NULL auto_increment,
`slug` varchar(192) collate utf8_unicode_ci NOT NULL,
`controller` varchar(64) collate utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `slug` (`slug`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;
Added to route.php file
$route[ 'default_controller' ] = 'home';
$route[ '404_override' ] = 'error404';
require_once( BASEPATH .'database/DB'. EXT );
$db =& DB();
$query = $db->get( 'app_routes' );
$result = $query->result();
foreach( $result as $row )
{
$route[ $row->slug ] = $row->controller;
$route[ $row->slug.'/:any' ] = $row->controller;
$route[ $row->controller ] = 'error404';
$route[ $row->controller.'/:any' ] = 'error404';
}
Set up a controller with:
<?php
class Home extends CI_Controller {
public function index()
{
$this->load->view('smooth/header.php');
$this->load->view('smooth/navbar.php');
$this->load->view('smooth/index.php');
$this->load->view('smooth/footer.php');
}
}
?>
In the database I have under controller home and as page contact
When I visit my site via my domain name only I see the frontpage but if I add the index.php/home to it I get nothing or index.php/home/contact nothing either
The path to my index.php files as situated in my domain/application/views/themes/smooth/index.php
Can anyone help with what I am missing please or what the problem is?
You didn't pass the routes to the view.
Without seeing the view in question I can't tell if there's an issue there, but you need to pass the data through like this:
$this->load->model('yourmodel');
$routes = $this->yourmodel->getRoutes();
$data = array(
'routes' => $routes
);
$this->load->view('smooth/header.php');
The database query goes in a model, so you will need to define that, and a method to return the data - here I've represented that model as yourmodel and the method to call as getRoutes. You should not, under any circumstances, be placing your database query in routes.php.

How to createe a URL string based on a named route and keep parameters

I need to create urls for category switching, switching category should reset page to 1st and change cat name but keep rest of url params.
Example of url:
http://example.com/cat/fruits/page/3/q/testquery/s/date/t/kcal/mine/26/maxe/844/minb/9/mint/4/maxt/93/minw/7/minbl/6/maxbl/96
Urls can contain many different params but category name and page should be always first, category without page should work too and show 1st page.
Currently I have defined two routes and I'm using Zend's url helper with named route but it resets params and as end resuls I have /cat/cat_name/page/1 url.
$category_url = $view->url(array('category'=>$list_item['url'],'page'=>1),'category',FALSE)
resources.router.routes.category_main.route = "/cat/:category/*"
resources.router.routes.category_main.defaults.module = "ilewazy"
resources.router.routes.category_main.defaults.controller = "index"
resources.router.routes.category_main.defaults.action = "results"
resources.router.routes.category_main.defaults.category =
resources.router.routes.category_main.defaults.page = 1
resources.router.routes.category.route = "/cat/:category/page/:page/*"
resources.router.routes.category.defaults.module = "ilewazy"
resources.router.routes.category.defaults.controller = "index"
resources.router.routes.category.defaults.action = "results"
resources.router.routes.category.defaults.category =
resources.router.routes.category.defaults.page = 1
Do I need to create custom method for assembling url in this case?
Things I would try:
Add module, controller and action params in $urlOptions (it's the
first array you pass to the view helper)
instead of 'category' route name try null and see what that does for
you.
Try removing this line
"resources.router.routes.category.defaults.category = "
Please specify what version are of zf are you using.
Solution is very simple, one just have to pass all current request params to url helper
(and optionaly overwrite/add some of them, page and category in my case), last variable is set to FALSE to prevent route resetting.
$view->url(
array_merge(
$params,
array('category' => $list_item['url'],
'page' => 1)
),
'category_main',
FALSE
);

Default routing in Zend Framework after defining some routes

I've been writing a project since some time and I've used the default routing, the :module\:controller:\:action.
After some time, I've added some routers to my config like:
resources.router.routes.page.route = "page/:slug"
resources.router.routes.page.defaults.module = "default"
resources.router.routes.page.defaults.controller = "pages"
resources.router.routes.page.defaults.action = "view"
resources.router.routes.page.defaults.slug = ""
But, after that, when I click on some link generated by view URL helper with one of the new routes all other links ignore some of given paramters. Example, I've got route:
resources.router.routes.project.route = "project/:slug"
resources.router.routes.project.defaults.module = "projects"
resources.router.routes.project.defaults.controller = "projects"
resources.router.routes.project.defaults.action = "view"
resources.router.routes.project.defaults.slug = ""
If I go to a link /project/test then link like this:
$this->url(
array('module' => 'admin', 'action' => 'list-users', 'controller' => 'users')
, null,true
);
will point to "/project"
Is there any possibility to maintain the default routing on top of custom routes? Can I add some default router that will work the same as the default one? It's probably something simple but I maybe missed the point. Thanks for all the help.
I've added something like this but with no effect:
resources.router.routes.default.route = ":module/:controller/:action"
resources.router.routes.default.defaults.module = "default"
resources.router.routes.default.defaults.controller = "pages"
resources.router.routes.default.defaults.action = "view"
resources.router.routes.default.defaults.slug = ""
In order for you to set your custom routing, you need to get the router component and pass your routes into it.
This is how I did mine in a project I am working on. In your Bootstrap class, you create the following function
protected function _initRoutes()
{
$this->bootstrap('frontcontroller');
$front = Zend_Controller_Front::getInstance();
$router = $front->getRouter();
$myRoutes = new Zend_Config_Ini(APPLICATION_PATH . '/configs/routes.ini','production');
$router->addConfig($myRoutes,'routes');
This calls the front controller and gets the router from it. I then pass my routes config into it.
I hope this answers your question.
The problem is that the options for url helper are as follows:
url($urlOptions, $name, $reset)
Therefore, when you set $name to null, current route ('project') is used. Not event setting $reset to true will help. Replace null with 'default' and it should work.

Pushing data to Dojo Filteringselect (with code)

I'm quite new to Zend & JSON, however I have the need to learn. What I want to achieve is: having a Dojo filteringselect (with autocomplete) control which is linked to zipcodes from a database (and which keeps track of the ID, so I can store that ID as a FK in another table (later on). The structure is MVC. I do get results from the database, however I can't seem to make it shine. Nothing shows up in the filteringselect control. So basicly the structure field of my database needs to get into the filteringsselect control and keeping track of that id, because I need it later on as a FK in another table.
Please help me out!
Table:
<?php
class Application_Model_Place extends FS_Model_Popo {
protected $_fields = array(
'id' => NULL,
'zip' => NULL,
'name' => NULL,
'up' => NULL,
'structure' => NULL);
protected $_primaryKey = array('id');
}
Form:
$place = new Zend_Dojo_Form_Element_FilteringSelect('Place');
$place->setLabel('Place')
->setAttrib('title', 'Add a place.')
->setAutoComplete(true)
->setStoreId('placeStore')
->setStoreType('dojox.data.QueryReadStore')
->setStoreParams(array('url' => '/graph/place/autocomplete'))
->setAttrib("searchAttr", "structure")
->setRequired(true);
Controller:
class Graph_PlaceController extends Zend_Controller_Action {
public function autocompleteAction() {
$this->_helper->viewRenderer->setNoRender();
$this->_helper->layout->disableLayout();
$structuur = $this->_getParam("structure", "");
$results = FS_Model_Factory::getInstance()->place->autocomplete(array('structure like ?'=> "%".$structure."%"));
$enc_res = array();
foreach ($results as $value) {
array_push($enc_res,$this->_helper->convert->toArray($value));
}
$this->_helper->json($enc_res);
$data = new Zend_Dojo_Data('id', $enc_res);
$this->_helper->autoCompleteDojo($data);
}
}
An example of json($enc_res) would be:
{"id":"235","zip":"3130","name":"Betekom","up":"BETEKOM","structure":"3130 Betekom"}, {"id":"268","zip":"3211","name":"Binkom","up":"BINKOM","structure":"3211 Binkom"},{"id":"377","zip":"3840","name":"Broekom","up":"BROEKOM","structure":"3840 Broekom"},{"id":"393","zip":"1081","name":"Brussel (Koekelberg)","up":"BRUSSEL (KOEKELBERG)","structure":"1081 BRUSSEL (KOEKELBERG)"},{"id":"421","zip":"1081","name":"Bruxelles (Koekelberg)","up":"BRUXELLES (KOEKELBERG)","structure":"1081 BRUXELLES (KOEKELBERG)"},{"id":"668","zip":"3670","name":"Ellikom","up":"ELLIKOM","structure":"3670 Ellikom"},{"id":"1236","zip":"3840","name":"Jesseren (Kolmont)","up":"JESSEREN (KOLMONT)","structure":"3840 Jesseren (Kolmont)"},{"id":"1275","zip":"3370","name":"Kerkom","up":"KERKOM","structure":"3370 Kerkom"}
I think you have several options :
Either you have control on the structure of the json you produce on your controller, and therefore you should generate the format expected by dojox.store.QueryReadStore (which is by default the same as dojo.data.ItemFileReadStore). See http://dojotoolkit.org/reference-guide/dojo/data/ItemFileReadStore.html#general-structure
Or you create a custom store that understands the structure of your json response. See http://dojotoolkit.org/reference-guide/dojox/data/QueryReadStore.html#query-translation
Or you are using dojo >1.6, and you can use dojo.store.JsonRest with its companion dojo.data.ObjectStore as described here
Option 1 is obviously the easiest...

How to create a custom router in Zend-Framework?

I am using a custom Router to enable pages like:
mytutorialsite.com/category/:categoryname
# added to application.ini
resources.router.routes.categorynameOnCategory.route = /category/:categoryname
resources.router.routes.categorynameOnCategory.defaults.module = default
resources.router.routes.categorynameOnCategory.defaults.controller = category
resources.router.routes.categorynameOnCategory.defaults.action = categoryname
I also have database table 'categories' in which all categories are stored. For example, let's say the following categories are currently stored in my database:
- html
- css
- js
- php
This means, the following pages exist:
mytutorialsite.com/category/html
mytutorialsite.com/category/css
mytutorialsite.com/category/js
mytutorialsite.com/category/php
But when you visit a page with a categoryname that is not listed in the database, like:
mytutorialsite.com/category/foo
You should get a 404 Page Does Not Exist message.
How do I achieve that?
I think you mean with categoryname as action in your route the :categoryname should be used as an action? There are two methods you can use. First is you add only the routes to the router where categories exist. When category/foo is requested the router won't recognize the route and throw the 404 error.
The second option is you catch all category/* routes and inside your action you check if the category exists.
For the first option, add a frontController plugin with a routeStartup function. In this hook you can do:
public function routeStartup(Zend_Controller_Request_Abstract $request)
{
// Get the router
$router = Zend_Controller_Front::getInstance()->getRouter();
// Fetch all your categories
$category = new Application_Model_Category;
$categories = $category->fetchAll();
// Loop and add all individual categories as routes
foreach ($categories as $category) {
$route = 'category/:' . $category->name;
$params = array(
'module' => 'default',
'controller' => 'category',
'action' => $category->name
);
$route = new Zend_Controller_Router_Route($route, $params);
$router->addRoute($category->name, $route);
}
}
The other method is more simple for the route. In your application.ini:
resources.router.routes.category.route = "category/:action"
resources.router.routes.category.module = "default"
resources.router.routes.category.controller = "category"
Now all requests from category/SOMETHING will go to the default module, category controller. The dispatcher checks if the action SOMETHING exists. When it does, it executes the action. When not, a Zend_Controller_Action_Exception ("action does not exist") is throw.
So in short, both methods work. With the first you get more control. The second is more simple but when e.g. an editAction, addAction or removeAction in the categoryController exist, they can be triggered as well (so be careful with that method).
PS. Of course, the routeStartup function should have a caching mechanism to prevent a database query on each request.

Categories