The Zend Framework allows for websites that are search engine friendly, although some thought needs to be taken when building your application. Besides creating a website that has a better chance of performing well with the search engines, these tips will also make your site friendly for visitors. Believe it or not, building a site that is search engine friendly can also yield usability benefits.
For the purposes of this article, we will assume you are familiar with the Zend Framework and understand how to create controllers, views and database connections. Please note, the code refers to ZF 0.1.5 and your mileage may vary with future releases. I will use a contrived example of a website that displays articles. The article class extends the Zend_Db_Table class:
Class Article extends Zend_Db_Table
{
protected $_primary = 'url';
}
I have decided to use the column "url" as the primary key instead of id. The reason will become obvious further down. The database structure is as follows:
CREATE TABLE `article` (
`url` varchar(255) NOT NULL default '',
`title` varchar(255) NOT NULL default '',
`content` text NOT NULL,
PRIMARY KEY (`url`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
Controllers, Views & Error Pages
I am forgetful, so I like to create a template controller and then copy this whenever I create a new controller. The code I use for the template controller is:
Class TemplateController extends Zend_Controller_Action
{
public function indexAction()
{
header('HTTP/1.0 404 Not Found');
$view = Zend::registry('view'); //assumes $view created and registered in index.php
echo $view->render('404.php');
}
public function noRouteAction()
{
header('HTTP/1.0 404 Not Found');
$view = Zend::registry('view'); //assumes $view created and registered in index.php
echo $view->render('404.php');
}
public function __call($method,$arguments)
{
header('HTTP/1.0 404 Not Found');
$view = Zend::registry('view'); //assumes $view created and registered in index.php
echo $view->render('404.php');
}
}
One of the first things you should do is create a view for pages not found, ie a 404 Error page (404.php referenced above). Put something useful in there for a visitor, like a friendly error message and a sitemap. When you create your new controllers by copying the template controller, they will by default display an error page until you start adding or overriding the appropriate methods. This is because urls are supposed to represent unique documents, and if a document doesn't exist, we should output a 404 header and an error page. Since you will most likely be using mod_rewrite with Apache, the normal error page from Apache will rarely show as most requests will be routed through index.php. Displaying custom errors pages is beneficial for a visitor, as this alerts them that the page they requested doesn't exist. It is also good for search engines, as it lets the spider know that the url is no longer valid and should be removed from the index.
The other option is using a redirect, but this should only be used when a specific resource is moved. Think about it from a visitor's perspective. A visitor clicks on a link for an article and ends up on an unrelated page. The visitor will likely be confused because he was anticipating seeing an article, but was surreptitiously transported to a different page. By displaying an error page instead, the visitor is now aware the article no longer exists and she can make an informed decision about what to do next.
Search Engine Friendly URLs
Now that the default controller and error view are set up, lets move onto displaying an article. The Zend Framework allows you to generate search engine friendly urls, such as "article/view/id/45" instead of "article.php?action=view&id=45". The truth is, the aforementioned url ("article/view/id/45") isn't very good in terms of search engines. Search engine friendly urls should be descriptive, with words used in the url in place of numbers where appropriate. I always like to create a url which would make sense to someone. For instance, "article/view/name/search-engine-friendly-urls-with-zend-framework" is better than "article/view/id/45" as it describes the content found at the url. It has the added benefit that search engine algorithms take the words in the url into consideration when ranking a page.
Assuming we want urls with the structure of "article/view/name/search-engine-friendly-urls-with-zend-framework", we can add this method to article controller (which is a copy of the template controller) and implement the view method like so:
public function viewAction()
{
$view = Zend::registry('view'); //view was created in index.php
$input = new Zend_Filter_Input($this->_getAllParams());
$url = $input->testRegEx('name','/^[a-zA-Z0-9-]+$/');
try {
$table = new Article(); //assumes Article.php has been included
$row = $table->find($url); // url column set as primary key in Article class
} catch (Exception $e) {
//handle exceptions
}
$view->title = $row->title;
$view->content = $row->content;
echo $view->render('article.php');
}
This is pretty straightforward, and uses the Zend_Db_Table to do the heavy work of getting the persisted information from the database and into the object. There is one problem with this method -what happens if a bogus url comes up? This readily happens as people mistype urls or an article may be deleted. To handle this, we need to check whether a row was returned.
try {
$table = new Article(); //assumes Article.php has been included
$row = $table->find($url); // url column set as primary key in Article class
} catch (Exception $e) {
//handle exceptions
}
if($row->url == '') {
$this->__forward('article','noRoute');
}
$view->title = $row->title;
$view->content = $row->content;
echo $view->render('article.php');
If the article doesn't exist, we shouldn't render the page. Instead we forward the request onto noRoute(), which will render a 404 page and send the appropriate header. You may want to create a new controller to handle error pages and forward bad page requests onto this controller instead of calling the noRoute method in each controller. This will come in handy in the future if you need to log bad referrals or implement any other kind of business logic for 404 pages.
Also note, I used the regular expression to match against the url. By doing this, I am assuming all article urls will only contain numbers, letters and dashes (which it will, once we implement the proper method for our article class to create a url). However, you may wish to not use a regular expression, as your urls could be different in the future.
Administrative Page for Creating Articles
Saving a new article to the database involves creating the url from the title supplied by a user. We override the insert method and use regular expressions to strip out characters from the title of the article to form the url.
public function insert($data)
{
$temp = preg_replace('/(\W){1,}/','-',$data['title']);
$data['url'] = strtolower(preg_replace('/-$/','',$temp));
return parent::insert($data);
}
For the controller, we will keep it simple for this article and hard code the attributes. Notice we check if a PDOException is thrown. If it is, this may be because the url already exists in the database. The url column is the primary key and therefore must be unique.
public function addAction()
{
include('/var/www/html/__shared/sixtyatsix/models/Article.php');
$article = new Article();
$data['title'] = 'SEO is Great!';
$data['content'] = 'Content';
try {
$article->insert($data);
} catch (PDOException $e) {
//possible duplicate error
} catch (Exception $e) {
//handle exception
}
}
Note, we don't want to change the url once an article is created, even if someone updates the title. If we allow the url to be changed whenever someone changes the title, it will result in a lot of error pages.
Views
When creating the view for displaying an article, we should make sure to have good information in the HTML title tag. I can't tell you how often I see sites with dynamic pages chock full of great content, only to find the HTML title and meta description tags are the same for every page. Search engines use the HTML title and meta description tag to rank pages, so you need to take advantage of how easy it is to create unique HTML title and meta description tags. Again, this is also beneficial to a visitor, as the HTML title and meta description tags are used in search engine results and the HTML title tag is used when a page is bookmarked.
Another thing to keep in mind is that search engines look at the text you use for links within your site. If you are displaying a list of articles, it can beneficial to use the article title as the link, instead of the ubiquitous "click here".
Conclusion
Hopefully these tips will help you create a search engine friendly website and one that is more usable for visitors. Not only will you get more people to your site, but they will stay there longer!

Comments
Could somebody fix this please?
I really like the idea of this. How many times have you pasted YouTube videos in IRC or IM and then had to explain what you're linking? Compared to say Google Video that has the title in the URL. Also it's a sweet way to get your keywords up in the search engines.
MySQL allows RegExps and triggers (at least 5 does), so an alternative to how you did your insert is to have the URL generated automatically inside MySQL whenever you insert to the table.
CREATE TRIGGER tablename_insert BEFORE INSERT ON tablename
FOR EACH ROW SET
NEW.name = REPLACE(NEW.name,' ','_'), // Replace a space with _
NEW.name = REPLACE(NEW.name,'\'','_'), // Replace a ' with _
NEW.url = REPLACE(NEW.name,'"','_'); // Replace a " with _
This gets very tedious if you allow all kinds of characters in your titles. You'd have to write a REPLACE for !, £, $, %, ^, &, etc, etc, etc. Not worth it in most cases.
I described a better format for URL's into ZF Forums.
http://www.zfforums.com/zend-framework-components-13/model-view-controller-mvc-21/search-engine-optimization-friendly-urls-zend-framework-44.html
and then i saw your tutorial.
I am wondering if it's possible to make url's the way i described on forum thread.
What about "949-Search-Engine-Friendly-Websites-with-the-Zend-Framework" << this is link on this web who implement union id_post - headline of post?? It's just my suggestion.
Regard's.