How do I dynamically change a controller in Zend Framework? - php

I'm halfway through a CMS where the URL is an SEO friendly name based on a page title. There is a need for one section to use a specific controller. So for example:
test.com/page1 (uses index controller)
test.com/page2 (uses index controller)
test.com/page3 (uses different controller)
test.com/page4 (uses index controller)
I could add a route that says "page3" will use the "different" controller, but the users of the CMS need to be able to change the name and seo of the URL, so where it is currently "page3" it could be changed later which would break my Routing rule.
What's the best way (either front controller plug in or other) to grab the request and pull the controller to be used from the DB (sql would be like "SELECT controller FROM menu WHERE seo = 'page3'"), then set that as the controller before Zend sets the controller to be used?
Any help or insight is greatly appreciated.

You will have to create a controller plugin and set the module/controller/action on the request object.
Then in the predispatch() you can do something like this:
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
$request->setModuleName($this->_getModule());
$request->setControllerName($this->_getController());
$request->setActionName($this->_getAction());
}
And then you can create methods __getModule(), _getController(), _getAction() that will examine the $_SERVER['REQUEST_URI'] and your DB and set the appropriate module/controller/action.

Related

Display dynamic URLs in CodeIgniter for seo

I'm building an online store based on CodeIgniter. I'd like URLs to look like this? What is the solution for this type of SEO friendly url.
http://example.com/[product-category]/[product-sub category]
I need this url:
example.com/women/sarees-sari
But my url is generated
example.com/Product/item/MQ==/women/sarees-sari
/Product/ is my controller,
/item/ is function name,
/MQ==/ is my product id
You can use routing to handle your request url. It's simple. For example for your case:
$route['women/sarees-sari'] = 'Product/item/MQ==';
Codeigniter has _remap function that can be called on controllers. So you can call this on core controller or main controller, and call your function that wish.
CodeIgniter has very good routing System so you can modify your url as per your requirement and linking using /application/config/routes.php file.
If you open this file first time, you will see only default controller, i.e $route['default_controller'] = 'welcome';
but you can add as many routes as you want. Like in your case for seo, you should add
$route['women-sarees-sari'] = 'Product/item/MQ=='; and this will route the user from www.example.com/women-sarees-sari to correct controller and method.

CodeIgniter - Select controller based on database

I'm building a simple CMS using Code Igniter version 3.0.0
The site's URLs are all customizable by the user and so do not follow the standard MVC structure of /controller/method/parameter-1/parameter-2/. Instead, all frontend traffic gets directed to PublicController's index method. This method searches the database for the current URL to return the correct page, and also the page type. Each page type corresponds to a controller.
How do I call that controller from the PublicController without doing a redirect?
I can't use the redirect() method because that would change the URL in the browser window and cause an un-need additional page request.
if you look at the url /about/who-we-are/
about is the controller and who-we-are is a function in the controller that loads one or more views.
The same for /locations/stores/
the functions stores in the controller locations.
read the documentation and it will be easy to understand.
http://www.codeigniter.com/user_guide/overview/mvc.html
I am pretty sure that configuring a route is your answer:
// routes.php
$route['(:any)'] = "PublicController/index/$1";
// PublicController.php
public function index()
{
var_dump(func_get_args());
}

Removing controller name and function namein URL in Codeigniter CMS

I am building a cms in codeigniter and i want to remove controller name and function name form the url and just print the alias.
I will be having two controllers one for static pages and other for blog posts with categories.
Please help, Suggestions reagrding modification of two controllers are also welcome.
You will need to override the default 404 controller in application/config/routes.php.
$route['404_override'] = 'content';
Any request that can't be mapped to a controller will be passed to the application/controllers/content.php controller
Your Content controller, or whatever you decide to call it, will parse the uri [$this->uri->segment(1)] and check for a matching reference in your CMS database.
If there is no match in the database, then you can look for a static view in the views folder and load it.
if(is_file(FCPATH.'views/'.$this->uri->segment(1).'.php')) {
$this->load->view($controller,$this->data);
}
If no static view is found, and there is no matching content in the db, call the show_404() function.
Using this method, you will keep the default CI functionality of uri mapping, so at any time, you can add controllers as you normally would and the app will perform like a vanilla CI install.

URL routing patterns for frontend pages in PHP

Im creating a MySQL database driven PHP (W)CMS application which follows the MVC pattern. First take a look at the framework:
The MVC framework handles the request and decides what to load/call based on the URL, like: http://domain.com/user/details/121 will load and instantiate a User controller object, and calls its details(121) method with the userid passed as a parameter, and then instantiate a User_Model and "ask" it for the detailed data of the user with the 121 userid, and at last display the result with a View. This is the basic concept of an MVC architecture. Nothing particular, everything is clear at this point.
Whereas this will be a CMS, I want to handle a Page model. A user with the nesessary permissions (mostly admin and/or root) can perform basic CRUD operations and other stuff on a page, for example:
I can create a page with the:
tile = 'About us' (this will be displayed as a headline of the page or the title of the browser tab like eg.: HTML title and h1 tags)
URL denomination = '*about_us*' (this will be the URI endpoint, like: http://domain.com/about_us)
reference name = 'Who we are' (This is the text displayed in the menubar)
page content = 'lorem ipsum...' (The actual content of the page...by a WYSIWYG html text editor)
and much more options like structuring the pages, to assign sub pages under a parent page, or making a page startpage (which means if I set 'About us' as a start page, then http://domain.com will automaticall load that page content)...
Or I can modify these properties, even I can delete a page...etc.
The MVC framework makes no difference between handling a frontend and a backend call.
For example we have some requests:
http://domain.com/user/details/121
http://domain.com/about_us
http://domain.com/our_products/1255
The first will load a backend controller as I detailed before,
but the others will load a frontend content.
When the Bootstrap loads the appropriate controller/action we look for the actual controller file, in the example above :
/controllers/Users.php
/controllers/About_us.php
/controllers/Our_products.php
The first can be loaded because that is a 'static' controller written before, but the About_us and Our_products are not existing controllers so If it is impossible to load the controller, the bootstrap searches the database if is there a page with the same URL denimination (like: about_us, our_products). If there is, we load a common FrontEndController and display the requested page data, if there isn't, display a 404 error.
I do this because I want the bootstrap to handle all requests the same way, but I dont want to every frontend URL compulsorily contain the FrontEndController (e.g.:http://domain.com/FrontEndController/our_products/1255). So this is how I hide it from the user, so the URL can remain more user friendly. My question is: Is this a good practice? Or are there any other proper ways to do this?
The MVC framework handles the request and decides what to load/call based on the URL
What you would normally is have is some sort of Router and Dispatcher class. The router would accept the the user/details/121, parse it and return a Route.
$route = $router->route( $request->getUri() );
The router could hold config values like the allowed space character in URI's, default allowed characters etc.
You can also add custom routes to the router
$router->addRoutes($routes);
The custom routes can be a simple associative array
$routes['requested-uri'] = 'custom-route'
In the example above you said when they visit the root of the website you want them to actually see the About Us page so that could be done like this:
$router->addRoutes([
'' => 'about-us
]);
Meaning when the URI is ''(blank) then go to the 'about-us' route. It shouldn't do a redirect, just transparently load up a different route while keeping the URI in the clients web browser the same.
Routing can obviously be more complex, using route objects added to a route collection for more advanced custom routing with more control. Some frameworks use annotations and all sort of different ways to achieve flexible routing.
The dispatcher could then accept the route returned from the router and dispatch it. That means verifying if the requested route actually exists i.e does the controller file exist and the requested method in the controller exist.
$view = $dispatcher->dispatch($route);
Inside the Dispatcher::Dispatch() method:
// Check if the controller file exists.
// Instantiate the controller file, preferably using a controller factory.
// Check if the controller method exists.
// Call the controller method
call_user_func_array([$controller, $route->getMethod()], $route->getParams());
$view = $controller->getView();
$action = $route->getAction();
// Call the view method.
if( method_exists($view, $action) ) {
$view->$action();
}
return $view;
I find the following a very easy to understand way of dealing with controller methods/actions. Let's say you have a login controller, the user sends a GET request to it first and a POST request to it when sending the login details in the form.
public function getIndex() { }
public function postIndex() {
$username = $this->request->post('username');
$password = $this->request->post('password');
}
The get and post in front of the method name is the request type, this prevents you having to do something like this
public function index() {
if( $this->request->getType() === 'POST' ) {
$username = $this->request->post('username');
$password = $this->request->post('password');
}
}
It also gives you more control over authorisation(if you do it at the routing layer) because you can easily allow a user to send a GET request to the controller but deny them access to sending a POST request.
Each controller has a one to one relationship with a view. The view get's injected into the controller on construction, preferably using a controller factory.
What would happen when you send a GET request http://domain.com/user/details/121 is the router would break up the URI and turn it into a route targeting the User controller, the getDetails() method with the parameter 121, the dispatcher checks if the controller and method exist, it then calls the method supplying the user ID as an argument, the controller sets the user ID in the view. Below is the User controller.
public function getDetails($userId) {
$this->getView()->setUserId( (int)$userId );
}
The view then has a method called details(). The same name as the method called in the controller, just without the request type in front of it.
The dispatcher then calls the details() method of the view which then fetches the required data.
Setting the title of the page is done in the view, as it is for presentation purposes only.
Part of the view that is related to the User controller
public function details() {
// Fetch the user by using the previously set user ID from the controller.
// If he doesn't exist set an error template, set the response code to 404,
// or redirect. Do whatever you want really.
$this->setTitle('User Details');
// Build template objects, bind the fetched user data to main template.
}
How you implement the setTitle method and all over view related stuff is up to you.
The view sends the response back to the client, whether it is HTML content, JSON, XML, or any other content type.
For example your application lets you search for users and export them to a Microsoft Excel Workbook file(.xlsx) and prompt the user to download it.
The view would:
Fetch the users
Generate the file
Set the HTTP response headers like Content-Type
Send the response

Zend framework Router replaces capital letters with dashes by default?

If we use capital alphabet in between name for zend controller and action for example inside default module we create
class MyGoodController extends Zend_Controller_Action {
public fooBarAction()
{
}
}
Than to access this action browser url looks like mysite.com/my-good/foo-bar
Is there any default zend router added inside zf managing this translation ?
because I want to use URL view helper to generate the correct link for me which it doesnt for e.g in view
$this->url(array('action'=>'fooBar','controller=>'myGood'));
did not produce the correct url it generates /myGood/fooBar instead of /my-good/foo-bar
As stated in the comment you need to use:
$this->url(array('action'=>'foo-bar','controller=>'my-good'));
The URL view helper assembles a link based on a route set in your application.
Routes match requests based on the URL.
It really comes down to separation of concerns. The helper is only making use of a route and again routes only deal with what is in the URL. Getting the proper class names based on a route is the dispatcher's concerns.
It's best to leave the route to deal with only what is in the URL because dispatchers can change. What might work for you using the standard dispatcher may not fit others that use a different dispatcher.
To accomplish what you're asking, you can always use a custom view helper that does the conversion for you but that is assuming you never change dispatchers.

Categories