Posts Tagged ‘htaccess’

SEO Friendly URLs with Zend Framework

// May 21st, 2009 // 8 Comments » // Zend Framework

Friendly URLs with ZendToday I will talk about defining custom friendly URLS with Zend Framework. If you’re thinking that the Zend Framework already provides SEO friendly URLs, well, you’re absolutely right. Zend’s MVC set up provides a very clean URL format in which a controller combined with an action gives you a specific page. Then variables that need to be passed to a particular page are passed in the URL.

For example, you may have

www.mywebsite.com/products/detail/id/15 where products is the controller, detail is your view and id is a parameter with the value of 15

or

www.mywebsite.com/article/view/slug/seo_urls where article is the controller, view is the view and slug is a parameter with the value of seo_urls

You can already see the string getting longer, and it’s not really how you want to see your URLs being displayed. Thankfully Zend has an easy fix for this, and you don’t even need an .htaccess file to do it.

Enter the rewrite router operation to the rescue!!

There are 6 types of routes, where #6 is special in that it is the one that is used by default by Zend to router controllers, actions and modules:

  1. Zend_Controller_Router_Route
  2. Zend_Controller_Router_Route_Static
  3. Zend_Controller_Router_Route_Regex
  4. Zend_Controller_Router_Route_Hostname
  5. Zend_Controller_Router_Route_Chain
  6. Default Routes

To use any of the 6 router types, you must first instantiate them with getRouter() which returns a rewrite router by default.

$router = $front->getRouter(); // returns a rewrite router by default

Zend_Controller_Router_Route

The Zend_Controller_Router_Route is the router type I use most often…so far.  It is easy to use and can be used to define dynamic and static routes. More on static routes later.

Lets look at an example where we want to have a URL that routes to a product’s detail page based on the product’s unique slug

www.mywebsite.com/product/product_slug

//Router: Product detail

$router = $front->getRouter(); // returns a rewrite router by default
$route = new Zend_Controller_Router_Route(
‘product/:slug’,
array(
‘controller’    => ‘product’,
‘action’        => ‘detail’,
),
array(’year’ => ’^[a-zA-Z0-9_]*$’)
);

$router->addRoute(’product’, $route);

Ok, lets go over the code above.

$router = $front->getRouter();

Next you need to create your custom route, you do that by using the Zend_Controller_Router_Route() function which takes 2 parameters and 1 optional parameter:

  1. A string with the route definition that will be matched to a URL
  2. An ass0siative array with a controller and an action that the pattern will map to.
  3. An optional variable requirement

Notice that in the route definition ‘product/:slug’ the ‘:slug’ part of the definition is a parameter which is passed to the target script. There you will be able to access all variables by means of the Zend_Controller_Action::_getParam() or Zend_Controller_Request::getParam() methods:

$slug= $request->getParam(’slug’);
$slug= $this->_getParam(’slug’);

You can additionally give the parameter a default value by adding them to the array where you specify the controller and action to use with the route:

array(
’slug’            =>    ‘product_slug’,
‘controller’    => ‘product’,
‘action’        => ‘detail’,
)

The 3rd parameter which is an optional regular expression that is used to define what kind of values are acceptable for each variable. In our case we want to match only letters, numbers and underscores. So any other characters would fail the test and the variable will be given whatever we set as default.

array(’year’ => ’^[a-zA-Z0-9_]*$’)

Zend_Controller_Router_Route_Static

The static router is a simpler version of Zend_Controller_Router_Route, meaning that if you have a route that will never change, then there is no need to fire up the regular expression engine. So if you have www.mywebsite.com/login, which will never have a parameter passed to it, you would use:

$route = new Zend_Controller_Router_Route_Static(
‘login’,
array(’controller’ => ’user’, ’action’ => ’login’)
);
$router->addRoute(’login’, $route);

Zend_Controller_Router_Route_Regex

This route type uses regular expressions instead of parameters when defining the route. It is a little more complex than the other route types, but it is more flexible and faster. Lets start with another example, lets say we want to go to an article archive page based on year and month:

$route = new Zend_Controller_Router_Route_Regex(
‘archives/(\d+)/(\d+)‘,
array(
‘controller’ => ’archive’,
‘action’ => ’show’
)
);
$router->addRoute(’archive’, $route);

Notice we have 2 regular expressions, both are(\d+) which match numbers. Our controller and actions are set. The router works just like the 2 previous routers exept that we don’t have variable names for the parameters. Instead they are assigned integest, NOT STRINGS, starting from 1. So to retrieve the values for www.mywebsite.com/archives/2009/01

$request = $this->getRequest();

$year    = $request->getParam(1); // $year = ’2009′;
$month   = $request->getParam(2); // $month = ’1′;

These are integers, NOT strings, so don’t use

$year    = $request->getParam(’1′);

Zend_Controller_Router_Route_Hostname

Zend_Controller_Router_Route_Hostname is the hostname route of the framework. It works similar to the standard route, but it works on the with the hostname of the called URL instead with the path.

Let’s use the example from the standard route and see how it would look like in a hostname based way. Instead of calling the user via a path, we’d want to have a user to be able to call http://martel.users.example.com to see the information about the user “martel”:

$hostnameRoute = new Zend_Controller_Router_Route_Hostname(
‘:username.users.example.com’,
array(
‘controller’ => ’profile’,
‘action’ => ’userinfo’
)
);

$plainPathRoute = new Zend_Controller_Router_Route_Static(”);

$router->addRoute(’user’, $hostnameRoute->chain($plainPathRoute);

The first parameter in the Zend_Controller_Router_Route_Hostname constructor is a route definition that will be matched to a hostname. Route definitions consist of static and dynamic parts separated by the dot (’.') character. Dynamic parts, called variables, are marked by prepending a colon to the variable name: :username. Static parts are just simple text: user.

Hostname routes can, but never should be used as is. The reason behind that is, that a hostname route alone would match any path. So what you have to do is to chain a path route to the hostname route. This is done like in the example by calling $hostnameRoute->chain($pathRoute);. By doing this, $hostnameRoute isn’t modified, but a new route (Zend_Controller_Router_Route_Chain) is returned, which can then be given to the router.

Zend_Controller_Router_Route_Chain

Sorry but I don’t fully grasp this concept yet so I am unable to talk about it here. I will refer you to the Zend documentation page for Zend_Controller_Router_Route_Chain instead