Zend router behaviour - zend-framework

I have some trouble with the router.
I have a custom route :
$router->addRoute('showTopic',
new Zend_Controller_Router_Route('/forum/topic/:topic',
array('module' => 'forum',
'controller' => 'topic',
'action' => 'show'),
array('topic' => '\d+')));
But when I try to access this url : localhost/forum/topic/16
I get this error :
Fatal error: Uncaught exception 'Zend_Controller_Router_Exception' with message 'topic is not specified'
But I don't want to put a default value for topic, because I also want the route /forum/topic to list all topics...
Secondly, I know that if I add a custom route, the default router is overridden, but I need to have some default routes too. The only way I have found is to set 'default' in the second parameter of the url view helper, like this
$this->url(array(
'module' => 'forum',
'controller' => 'topic',
'action' => 'add'
), 'default', true)
Is there a more elegant way instead of doing this for all url where I want to use the default behavior ?

You should have a default value for a topic and add the more general route (the one for forum/topic) after the more specific one. Route_Rewrite checks the routes beginning with the last one (it actually does an array_inverse).
The url helper delegates assembly urls to a route, its second paremeter being the name of the route to pull from the router. Since the default route is registered under the name of 'default', there is nothing really inelegant in using the name (it is not a magic string or a special case). If this really bugs you, you could write a custom helper (to be placed under "views/helpers"):
class Zend_View_Helper_DefaultUrl extends Zend_View_Helper_Abstract {
public function defaultUrl($params) {
return $this->view->url($params, 'default');
}
}
And use it in your view like defaultUrl(array('action'=>'test')) ?>.

Related

CakePHP route redirect with parameters

I need to keep SEO links active so I'm trying to 301 redirect google trafic to new CakePHP route.
I go to:
http://localhost/jakne/someCategory/item-slug
And I want it to 301 redirect to:
http://localhost/product/item-slug
So I tried with route::redirect but I can't make it work. Doc on this is also non existent :(
$routes->redirect(
'/jakne/:subcategory/:item',
['controller' => 'Catalog', 'action' => 'product'],
['status' => 301, 'pass' => ['item']]
);
My Catalog::product looks like:
public function product($productId) {
}
I always get error that no parameter was passed to the action.
What am I missing? :(
The option for retaining parameters in redirect routes isn't pass (that's for regular routes and defines which parameters to pass as function arguments), it's persist, ie your route would need to be something like:
$routes->redirect(
'/jakne/:subcategory/:item',
['controller' => 'Catalog', 'action' => 'product'],
['status' => 301, 'persist' => ['item']]
);
This should work fine, assuming you have a proper target route connected that has a parameter named item, something like.
$routes->connect(
'/product/:item',
['controller' => 'Catalog', 'action' => 'product'],
['pass' => ['item']]
);
Generally you may want to consider doing such redirects on server level instead (for example via mod_rewrite on Apache), performance wise that's much better.
ps. Browsers do cache 301 redirects, so when making changes to such redirects, make sure that you clear the cache afterwards.
See also
Cookbook > Routing > Redirect Routing
So it turns out this is quite simple. I use this to dynamically generate a list of redirects based on what admins enter in the control panel. We use this to keep google traffic when the URL changes and is not rescanned by the google bot yet.
$builder->redirect('/from-url', '/to-url', ['status' => 301]);
Try this ways it is working for me:
Example request like: localhost:08080/get-username?id=%3Cid%3E
Routes :
$routes->connect('/get-username', ['controller' => 'Users', 'action' => 'getUserName']);
Controller :
class UsersController extends AppController {
public function initialize() {
parent::initialize();
$this->loadComponent('RequestHandler');
}
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$this->set('_serialize', false);
$this->Auth->allow([
'getUserName'
]);
}
public function getUserName() {
$id = $this->request->getQuery('id');
}
}

How to get the variables in routing in zend framework 1.12

I am new to routing in zf. I don't understand some terms in route
$route = new Zend_Controller_Router_Route(
'author/:username',
array(
'controller' => 'profile',
'action' => 'userinfo'
)
);
$router->addRoute('user', $route);
How do we get :username here? from where do we get this?
Username here is parameter pass by the user. So correct link using this route will be http://somepage.com/author/John. In your controller you can get this variable just like POST and GET variables - $this->getParam('author');
If you want to allow user use link without parameter (default parameter) you can add to array - 'author' => null (i usually use this in pagination)

Zend Routing problems

I've read all posts about routing and Zend Documentation but I still can't solve this issue.
I have a multi-language application with two modules: default and admin. The language selection is working fine (in a Controller routeShutdown Plugin), but I have some problems configuring the router:
I want to have these URL working:
/
/controller
/controller/action
/action (default controller)
/controller/param (default action)
/admin
/admin/admin-controller
/admin/admin-controller/action
and using the language selector it would be:
/en
/en/controller
/en/controller/action
/en/action (default controller)
/en/controller/param (default action)
/en/admin/admin-controller
/en/admin/admin-controller/action
I added this to my bootstap file (index.php):
$frontController = Zend_Controller_Front::getInstance();
$router = $frontController->getRouter();
$router->removeDefaultRoutes();
$router->addRoute('langmodcontrolleraction',
new Zend_Controller_Router_Route('/:lang/:module/:controller/:action',
array('lang' => ':lang'))
);
$router->addRoute('langmodcontroller',
new Zend_Controller_Router_Route('/:lang/:module/:controller',
array('lang' => ':lang',
'action' => 'index'))
);
$router->addRoute('langmod',
new Zend_Controller_Router_Route('/:lang/:module',
array('lang' => ':lang',
'action' => 'index',
'controller' => 'index'))
);
$router->addRoute('lang',
new Zend_Controller_Router_Route('/:lang',
array('lang' => ':lang',
'action' => 'index',
'controller' => 'index',
'module' => 'default'))
);
$frontController->setControllerDirectory(array(
'default'=>BASE_PATH.'app/modules/default/controllers',
'admin'=>BASE_PATH.'app/modules/admin/controllers'));
In order to check how the router is parsing the URL, I added a var_dump to the routeShutdown plugin:
Entering to /en, I get:
array
'lang' => string 'en' (length=2)
'action' => string 'index' (length=5)
'controller' => string 'index' (length=5)
'module' => string 'default' (length=7)
which is OK. But when I enter to /en/controller1 I get:
array
'lang' => string 'en' (length=2)
'module' => string 'controller1' (length=8)
'action' => string 'index' (length=5)
'controller' => string 'index' (length=5)
It is setting module to "controller1". How can I tell the router to set the default value to the module? And for an URL like /en/controller/param? (setting module and action to default)
I'm afraid you're going to need to rethink your URL scheme a little, or change the way your routes are setup, as you've hit two limitations of the way ZF's routing works.
The first is that the router has no knowledge of what is or isn't a valid module, controller or action; all it does is match the strings in the URL to variables in the route. It does this by checking each route in succession, in reverse order, until it finds a match. When you hit /en/controller, it first checks your /:lang route, which won't match. It then checks /:lang/:module, which will match, because /:lang/:module will match /anything/anything unless you tell it otherwise.
With that in mind you won't be able to have both:
/en/controller
/en/action
unless you set some restrictions, as a URL like /en/foo will always be matched by whichever of the two you define last.
If you have a fairly small number of actions/controllers that don't often change, the simplest way around this is to hardcode in some possible values for the 2nd of the two routes, e.g.:
$router->addRoute('langmod', new Zend_Controller_Router_Route(
'/:lang/:module',
array(
'lang' => ':lang',
'action' => 'index',
'controller' => 'index'
),
array(
'module' => '(foo|bar|something)'
)
));
replace foo, bar etc. with valid module names. Now when you hit /en/controller1 it won't match this route because controller1 doesn't match the regexp pattern defined for the :module variable. You would then need a separate /:lang/:controller route (or possibly /:lang/:controller/:action) for it to match instead.
You asked how you set a default value for some of the variables. You are actually already doing this with the action in a few of your routes, but for controller/module won't quite work in the way you are hoping. If we take your langmodcontroller route and change it to this:
$router->addRoute('langmodcontroller',new Zend_Controller_Router_Route(
'/:lang/:module/:controller',
array(
'lang' => ':lang',
'controller' => 'index'
'action' => 'index'
)
));
there's now a default value for the controller variable. If we pretend for a second that this was the only route, a request for /en/blog would now get matched by this and set the request params to lang = en, module = blog, controller = index, action = index. /en/blog/index/foo would also match this route, and would give you module = blog, controller = index, action = foo. But note that even though controller = index you still need that in the URL. So limitation number two is that you always need the variable in the URL (even if it is set to your default) as long as you have something after it that isn't the default.
With these limitations in mind I'd suggest you go with something like this (defined in this order):
/:lang/:controller/:action/ (with 'index' defaults for controller and action)
/:lang/:action (with 'action' restricted to some predefined values)
/:lang/admin/:controller/:action (with 'admin' as a string in the URL, and :module set to 'admin' as the default)
This would give you URLs like this:
/en
/en/controller
/en/controller/action
/en/action
/en/controller/param
/en/admin/controller
/en/admin/controller/action
which is pretty much what you are after.
The routing in ZF is very powerful, you just need to know its quirks.

How to route multi subdomain with zend router hostname

I need to create routing in Zend to simply copy the current live site url structure which is sadly inconsistent
What i want to do is to route subdomain as follow:
www.site.com -> static router
a.site.com & b.site.com -> category controller
c.site.com & d.site.com -> location controller
the rest sub domain -> user controller
could anyone guide me how to solve this, thanks.
UPDATE:
First thanks Fge, vote your answer, it works but i need some more advice:
Since i have many subdomains for each rules is there a better way than add the rules in looping
foreach($subdomains as $a){
$tr = new Zend_Controller_Router_Route_Hostname(
"$a.site.com",
array(
'module' => 'mod',
'controller' => 'ctrl',
'param_1' => $a
));
$router->addRoute($a,$tr);
}
How to combine it with other routing type to parse the parameters (chained?), something like http://a.site.com/:b/:c, i want t parse it to param_1 (a), param_2 (b), param_2 (c)
Note: Reverse Matching
Routes are
matched in reverse order so make sure
your most generic routes are defined
first.
(Zend_Controller_Router)
Thus you have to define the route for all other subdomains first, then the specific ones:
$user = new Zend_Controller_Router_Route_Hostname(
':subdomain.site.com',
array(
'controller' => 'user'
)
);
$location1 = new Zend_Controller_Router_Route_Hostname(
'c.site.com',
array(
'controller' => 'location'
)
);
$location1 = new Zend_Controller_Router_Route_Hostname(
'd.site.com',
array(
'controller' => 'location'
)
);
// other definitions with known subdomain
$router->addRoute($user); // most general one added first
$router->addRoute($location1);
$router->addRoute($location2);
// add all other subdomains
Update for the updated question:
1) This really depends on how different the parameters are you want to route a subdomain to. In your example you routed them all to the same model and controller and added the actual subdomain as a parameter. This can be done easily with the user-route i posted above. There the subdomain is set as parameter subdomain ($request->getParam("subdomain")). If you want the subdomains to be the action of a known controller/model you could replace :subdomain with :action. But as soon as you have other controllers/models for each subdomain, I'm affraid you have to loop over them (or use a config file). For the example you provided in the question, the route simply could look like this:
$user = new Zend_Controller_Router_Route_Hostname(
':param1.site.com',
array(
'controller' => 'user'
)
);
// routes "subdomain".site.com to defaultModul/userController/indexAction with additional parameter param1 => subdomain.
As long as you don't have any schema in your subdomains it's very difficult to route them in a general way.
2) That's an example where router chains come into play. The outer route would be the hostname route which handles the subdomain and the inner route would handle the :a/:b part. This could look like this for example:
$user->chain(new Zend_Controller_Router_Route(':a/:b'));

Zend_Navigation url generation issue

I use Navigation component for site menus. I also use let zend figure-out the selected menu item from request parameters - I guess this is done automatically. The only problem is, that for this to work, action and controller have to be specified in navigation configuration for every node. This also means that when zend generates links from route, action and controller information to appended to the generated link automatically.
Anyone had the same problem?
Zend manual section, explaining the Mvc navigation page features.
Example:
some route defined in bootstrap:
$router->addRoute('user_profile_tab', new Zend_Controller_Router_Route(
'profil/:user/:location/:tab/*',
array(
'action' => 'profile',
'controller' => 'user',
'user' => ($user ? $user->id : 0), //change later
'location' => 0 //inject appropriate value later
)
));
navigation container object:
$container = .....
......,
array(
'label' => tr('Privileges'),
'id' => 'user-profile-perms',
'type' => 'Zulu_Navigation_Page',
'controller' => 'user',
'action' => 'profile',
'route'=> 'user_profile_tab',
'params' => array('tab'=>Main_Lib_Common::NAVI_USER_TAB_PERMS)
)
);
the result when using
$page = $container->getById('user-profile-perms');
$page->href;
http://www.example.com/profil/1/0/3/controller/user/action/profile
WHY action and controler params in the navigation container object you ask. The $page->isActive() check needs this data to make a perfect match.
THE FIX:
extend mvc navigation page and provide an alternative getHref() method ... one that removes action, controller and module params when a route does not define them.
I have done this to fix this weird behaviour:
extend mvc navigation page
provide an alternative getHref() method
check for routes, not having action , controller and module parameters and remove them from params array before href generation.
This way the isActive matching will still work, as we didnt modify the route or navigation nodes in any way.