Using URL Slugs in a CakePHP App

URL slugs in a CakePHP Blog Application

URL slugs are extremely common on the internet today, particularly on website blogs. The title of the post is used as an identifier in the URL instead of an id number, this makes it possible for a person looking at the URL to know what the post is generally about. For example this post in your address bar is seen as "/posts/view/using_url_slugs_in_a_cakephp_app" instead of "/posts/view/1" which makes the URL meaningful and human readable.

In this article I'm going to go through the benefits of using URL slugs and how to incorporate them into the CakePHP blog application that we have been building over the previous articles.

Advantages of using URL Slugs

  1. Your URLS are cleaner and more readable
  2. People can see what the post is about by the title
  3. Can help with Search Engine Optimisation as keywords are a part of the url

Modifying the Database

Following on from my previous article i'm going to edit the blog application to use URL slugs instead of ids, I'm going to do this for both 'posts' and 'tags'. The first thing we need to do is to modify the database so that the posts and tags tables have a 'slug' column. To do use the following SQL command in phpmyadmin:

ALTER TABLE `posts` ADD `slug` VARCHAR( 255 ) NOT NULL AFTER `title` ;
ALTER TABLE `tags` ADD `slug` VARCHAR( 255 ) NOT NULL AFTER `tag` ;

The String to Slug Function

In the past I've been using a fairly basic function to turn a string into a url slug and that function can be seen below, however when researching this article i came across an inbuilt CakePHP function that will do the job better.

function stringToSlug($str) {
	// trim the string
	$str = strtolower(trim($str));
	// replace all non valid characters and spaces with an underscore
	$str = preg_replace('/[^a-z0-9-]/', '_', $str);
	$str = preg_replace('/-+/', "_", $str);
return $str;
}

The CakePHP Inflector class has a function that will turn a string into a slug in one line of code and it takes 2 arguments, the string to convert and a character to replace the spaces with the default character being an underscore i.e. '_'. We now just need to convert the string to lowercase and our function is complete:

// file: 'app/app_controller.php'
function stringToSlug($str) {
	// turn into slug
	$str = Inflector::slug($str);
	// to lowercase
	$str = strtolower($str);
return $str;
}

// CakePHP code in the slug function uses a regular expression:
$string = preg_replace(array('/[^\w\s]/', '/\\s+/') , array(' ', $replacement), $string);

I'm going to place this function in our 'app_controller' file located at '/app/app_controller.php' so that we can use this function across all our controllers by simply calling:

$this->stringToSlug("String to Convert");

Editing the Blog Application

The first thing I'm going to do is modify the 'posts_controller' so that when we try to add a new post, a URL slug is automatically created and inserted into the database. This is extemely easy by creating the slug variable in the $this->data array before it saved to the database.

// file: 'app/controllers/posts_controller.php'
// create the url title slug
$this->data['Post']['slug'] = $this->stringToSlug($this->data['Post']['title']);

// try saving the data
if ($this->Post->save($this->data)) {

When a new post is added to the database, the controller takes the title of the post, turns it into a slug with the function we created earlier and saves it in the 'slug' column of the table. If we have a quick look in our database you will see that the slug was successfully created.

URL Slugs in the Database

Add the same code to the 'edit' action to make sure that a new URL slug is created when we try to edit a post, this also makes it easy to create slugs for posts that currently don't have one. So quickly go into the edit screen of each post and click save and a URL slug will be created automatically.

The next step is to change the 'view' function to start using the slugs instead of the post id, this is easily done by using CakePHP's magic 'findBy' methods, simply change the id variable to slug and change the read() method to findBySlug() and CakePHP will find your posts using the URL slugs that we generate.

// file: 'app/controllers/posts_controller.php'
function view($slug = null) {
	if (!$slug) {
		$this->Session->setFlash('Invalid id for Post.');
		$this->redirect('/posts/');
	}
	$this->set('post', $this->Post->findBySlug($slug));
}

Now we just need to make a quick change to the 'Posts' 'index.thtml' file to change the post link to use the url slug:

// file: 'app/views/posts/index.thml'
// was
echo $html->link('View','/posts/view/' . $post['Post']['id']);
// now
echo $html->link('View','/posts/view/' . $post['Post']['slug']);

We can now test that everything is working by clicking on the 'view' link in the posts index, the application now uses the slug in the URL and successfully finds the post in the database and displays it correctly.

URL slugs in a CakePHP Blog Application

Further Work

Once you have got the 'posts' working with URL slugs it is easy to use the same process to convert any other items such as the 'tags' controller. Please by aware that the process we have been through does not factor in a check to see if the slug is already used in the database. A simple function could be created to do this check and append the new slug with an incremental value but I'm not going to go through that now.

Wrapping Up

So there you have it, incorporating URL slugs into your CakePHP application is really not that hard and the benefits of having a clean, readable url that will help with your SEO campaign far outway the little time and effort that you must put in to make it all work. Hopefully this article has been clear and easy to understand but if your having trouble let me know and I'll try and help you out as much as I can.

In my next article i'm going to go through the process of validating data that a user submits and how to automatically display error messages if a required field is missing.

Posted on 7th March 2008
on 7/3/08

comments powered by Disqus