Full dynamic router in Zend Framework - zend-framework

By default you have the following URL-syntax in ZF: /module/controller/action. What i want, is to build an menu-system where i can use any URL I want.
Lets say I make an menu-item called 'news'. When i call http://www.site.com/news i want to have the folowing loaded:
module: news
controller: frontpage
action: display
These config-values must be configured in the database-record for the menu-item.
How can I do this in zend? I spend a lot of time searching for it, but I still can't figure out how to. Does anybody?

I'd suggest using a front controller plugin to scan your database for all the entries, create routing rules based on those entries and add them to the router (see this).
Of course caching strategy is recommended so that you don't do a lot of processing on every request.

You can create a plugin and in routeStartup define something that intercept your request and route /module/controller/action to /action, but for this all your action names must be unique :
class My_CustomRouterPlugin extends Zend_Controller_Plugin_Abstract
{
public function routeStartup(Zend_Controller_Request_Abstract $request)
{
$fc = Zend_Controller_Front::getInstance();
$action =$fc->getRequest()->getActionName();
$router = $fc->getRouter();
$model= new myModel();
$myPage = $model->getPageByAction($action);
$route = new Zend_Controller_Router_Route('/action', array(
'module' => $myPage->getModule();
'controller' => $myPage->getController();
'action' => $action;
));
$router->addRoute($action, $route);
return $router;
}
}
In myModel define a method can get you an object(or an array) that contains module, controller names (from you DB ).
and register this plugin in your bootstrap:
$front->registerPlugin(new My_CustomRouterPlugin());

Related

Zend Framework 1 add single custom rule for routing

I am having problem with adding custom routing into my application. Zend Framework 1:
I have already working application with default Zend routing. I have a lot of controllers and for some of them I want to use friendly urls, for example:
domain.com/cities/edit/id/3
to
domain.com/cities/edit/abu-dhabi
There are some controllers to switch into that url (in that format).
I tried to configure it by ini file:
resources.router.routes.slugurl.route = /:controller/:action/:slug
And also by Bootstrap method:
protected function _initSlugRouter()
{
$this->bootstrap('FrontController');
$router = $this->frontController->getRouter();
$route = new Zend_Controller_Router_Route('/:controller/:action/:slug/',
array(
'controller' => 'slug',
'action' => 'forward',
'slug' => ':slug'
)
);
$router->addRoute('slug',$route);
}
The main problem - with ini configuration - request goes directly to controller city (not slug). Second one (bootstrap) - also executes the city controller, but the default routing not working as it was, I am not able to execute:
domain.com/cities/ but domain.com/cities/index/ works. The error without action defined:
Action "forward" does not exist and was not trapped in __call()
I can monitor the "slug" in controllers/move check into some library, but I would like to do it only with routing - that way looks much better for me.. Please, how to solve that issue?
edit with summary of solution
Thank you Max P. for interesting. I solved it at last - in last 2 minutes:) I had wrong rules at application.ini with routing. Right now it is defined that:
resources.router.routes.slugurl.route = /:controller/:action/:slug
resources.router.routes.slugurl.defaults.controller = :controller
resources.router.routes.slugurl.defaults.action = :action
resources.router.routes.slugurl.defaults.slug = :slug
The controller is defined like that:
class CitiesController extends Crm_Abstract
The Crm_Abstract has code:
<?php
class Crm_Abstract extends Zend_Controller_Action {
public function __construct(\Zend_Controller_Request_Abstract $request, \Zend_Controller_Response_Abstract $response, array $invokeArgs = array()) {
$params = $request->getParams();
if (!empty($params['slug'])) {
$modelSlug = Crm_Helper_Slug::getInstance();
$parameters = $modelSlug->redirectRequest($request);
if ($parameters !== false) {
foreach ($parameters as $key => $value) {
$request->setParam($key, $value);
}
}
}
parent::__construct($request, $response, $invokeArgs);
}
public function __call($methodName, $args) {
parent::__call($methodName, $args);
}
}
The slug helper get parameters for slug at defined controller. The url to access the slug is defined/calculated in place of $this->url - to $this->slug and there rest of code is done in slug helper. I am not sure if my solution is 100% sure, because that is my first time with routing.
The major thing is that - I just needed to change Zend_Controller_Action to Crm_Abstract which extends the Zend_Controller_Action. Single line - no additional custom code in each controller.
I created two route rules:
resources.router.routes.slugurl.type = "Zend_Controller_Router_Route"
resources.router.routes.slugurl.route = /:controller/:action/:slug/
resources.router.routes.slugurl.defaults.module = "default"
resources.router.routes.slugurl.defaults.controller = :controller
resources.router.routes.slugurl.defaults.action = :action
resources.router.routes.slugurl.defaults.slug = :slug
resources.router.routes.plain1.route = "/:controller/:action/"
resources.router.routes.plain1.defaults.module = "default"
resources.router.routes.plain1.defaults.controller = "index"
resources.router.routes.plain1.defaults.action = "index"
And for now that works fine. Bad thing - I don't understand routing (htaccess rules), but the time spent on that problem and solve it is better than modify all controllers...

how to make optional parametres in zendframework URL

I am new to Zend, but very very keen to learn. This is really just a quick question on routing in Zend Framework.
I understand the basic of it but I am still confused about how I can create some optional parameters at the end of my URL. For example, I have the following default page URL:
examplesite.com/accounts/enquiry
I now want to add two additional parameters to it i.e:
userid= 6
location= 12
So, the eventual URL should look like:
examplesite.com/accounts/enquiry/6/12
but
examplesite.com/accounts/enquiry
Will get you to the same page.
I am not clear. How do it do this? I mean, this is not a bespoke URL. so, I don't need to create a custom route. It basically just the last two parameters that need to be added to the page.
How do I do this?
First 2 parameters are controller and action name, the named params.
Here you are:
examplesite.com/accounts/enquiry/userid/6/location/12
or you can define your own route like this:
$route = new Zend_Controller_Router_Route('accounts/enquiry/:userid/:location);
and then add it to router:
$router->addRoute('accounts', $route);
You could add a custom route inside your Bootstrap.php, e.g. (untested):
protected function _initRoutes()
{
[...]
$frontController = Zend_Controller_Front::getInstance();
$router = $frontController->getRouter();
$accounts = new Zend_Controller_Router_Route(
'accounts/enquiry/:userid/:location',
array(
'userid' => '[0-9]{2}',
'location' => '[0-9]{2}',
'controller' => 'accounts',
'action' => 'enquiry',
)
);
$router->addRoute('accounts', $accounts);
[...]
}
http://framework.zend.com/manual/1.12/en/zend.controller.router.html

Have a default controller in Zend

I have a module-based arhitecture in Zend Framework 1.11 (site.com/module/controller/action).
I've setup my site to have a default module, so that if I have the site module as default, and you go to site.com/something1/something2, it will actually take you to site.com/site/something1/something2.
I want to achieve the same thing 1 level further: say if you go to site.com/something, it should take you to site.com/site/index/something. I'm not talking about a redirect, just a re-routing.
Would something like this be possible?
If I understand correctly, it is possible and here is an example you can put in your Bootstrap:
protected function _initControllerDefaults()
{
$this->bootstrap('frontcontroller');
$front = Zend_Controller_Front::getInstance();
// set default action in controllers to "something" instead of index
$front->setDefaultAction('something');
// You can also override the default controller from "index" to something else
$front->setDefaultControllerName('default');
}
If you need the default action name to be dynamic based on the URL accessed, then I think you are looking for a custom route. In that case try:
protected function _initRoutes()
{
$router = Zend_Controller_Front::getInstance()->getRouter();
// Custom route:
// - Matches : site.com/foo or site.com/foo/
// - Routes to: site.com/site/index/foo
$route = new Zend_Controller_Router_Route_Regex(
'^(\w+)\/?$',
array(
'module' => 'site',
'controller' => 'index',
),
array(1 => 'action')
);
$router->addRoute('actions', $route);
}

Hostname and Custom routing in Zend Framework don't work together for me

I am building an application that uses hostname routing to detect subdomains like
user1.example.com
user2.example.com
and also have custom routes like user1.example.com/login
This works well so far, however when I add custom routes they do not work. I have searched and read a lot but seems there is something I am missing. Here is what I have so far:
//my routes in routes.ini
[development]
routes.login.type = "Zend_Controller_Router_Route"
routes.login.route = "/login"
routes.login.defaults.controller = "user"
routes.login.defaults.action = "login"
//This part in Bootstrap file
$this->bootstrap('frontController');
$router = $this->frontController->getRouter();
$routerConfig = new Zend_Config_Ini(
APPLICATION_PATH . '/configs/routes.ini',
'production'
);
//I create a default route
$routeDefault = new Zend_Controller_Router_Route_Module(
array(),
$this->frontController->getDispatcher(),
$this->frontController->getRequest()
);
$router->addConfig($routerConfig, 'routes');
// hostname route
$hostnameRoute = new Zend_Controller_Router_Route_Hostname(
':username.mysite.com',
array(
'module' => 'default',
'controller' => 'index',
'action' => 'index',
)
);
//I add the default route.
$router->addRoute('default', $routeDefault);
//I chain the routes so that all routes have subdomain routing too
foreach ($router->getRoutes() as $key => $theroute) {
$router->addRoute($key, $hostnameRoute->chain($theroute));
}
When I go to a custom route like http://user1.example.com/login I get the error: 'Invalid controller specified (login)' which means my custom route is not being recognized. I am also not sure if the way I am adding the default route is correct and necessary. If I remove that code then it doesn't work. So my problem really is that I would like my hostname matching, custom routes and default routes to all work. If you can spot where I'm going wrong please help, I have read previous related posts all over on routes, chaining, default routes etc (including this very related one: How do I write Routing Chains for a Subdomain in Zend Framework in a routing INI file?) but haven't found the solution so far.
You should be able to setup your routing using a custom param in the route:
routes.testdynamicsubdomain.type = "Zend_Controller_Router_Route_Hostname"
routes.testdynamicsubdomain.route = ":subdomain.domain.dev"
routes.testdynamicsubdomain.defaults.module = public
routes.testdynamicsubdomain.defaults.controller = index
routes.testdynamicsubdomain.defaults.action = index
If your apache/hostfile etc are configured correctly going to test.domain.dev should load the index action in your indexController where you could get the :subdomain param:
echo $this->getRequest()->getParam('subdomain');
Also, as you discovered, the order of the routes is very important. See also Zend Router precedence for more info about this.

Zend Framework: Need help setting up Routing

how do i set up routing as follows
these work with the standard routing
/posts => index action (listing)
/posts/view => view action (individual post)
/posts/add => add action
/posts/edit => edit action
what abt these?
/posts can by filtered based on 1 or more query strings, in any order. eg.
/posts/tagged/tag1
/posts/tagged/tag1/timeframe/1w => fyi. 1w means 1 week
/posts/timeframe/1w/tagged/tag1 => can be in any order
/posts/sortby/dtposted => more options maybe added
how can i handle these? i tried
$route = new Zend_Controller_Router_Route(
'posts/*',
array(
'controller' => 'posts',
'action' => 'index'
)
);
$router->addRoute('postsIndex', $route);
but of cos, all routes to posts/* goes to the index controller. not what i want
You dont need to use a route for those url's if your using proper naming conventions it should naturally.
class PostsController extends Zend_Controller_Action{
public function viewAction(){
}
public function editAction(){
}
public function addAction(){
}
public function indexAction(){
}
}
I suggest going back to basics and learning how controllers models and views work in the zend framework before trying to understand routing :)