Generate unique slugs in CakePHP

Posted on 23 October 2008 (10:41 AM)

I believe it was Wordpress who came up with the term "slug". It has been adopted by many other systems though, and it basically means the following: a unique identifier which can be used in a URL to create a permalink to a page.

At least, that's my interpretation of it. In this article I will show you how you can generate a unique slug easily when working with the CakePHP Framework.

Since the slug should be unique across records in the same database table, the best place to store slug-generating functionality is in the AppModel, which is, strictly speaking, the only business logic layer that may access the database.

If you haven't got an AppModel created yet, add one in /app/app_model.PHP. You may fill it with the following code:

  1. <?php
  2. class AppModel extends Model {
  3. function createSlug ($string, $id=null) {
  4. $slug = Inflector::slug ($string,'-');
  5. $slug = low ($slug);
  6. $i = 0;
  7. $params = array ();
  8. $params ['conditions']= array();
  9. $params ['conditions'][$this->name.'.slug']= $slug;
  10. if (!is_null($id)) {
  11. $params ['conditions']['not'] = array($this->name.'.id'=>$id);
  12. }
  13. while (count($this->find ('all',$params))) {
  14. if (!preg_match ('/-{1}[0-9]+$/', $slug )) {
  15. $slug .= '-' . ++$i;
  16. } else {
  17. $slug = preg_replace ('/[0-9]+$/', ++$i, $slug );
  18. }
  19. $params ['conditions'][$this->name . '.slug']= $slug;
  20. }
  21. return $slug;
  22. }
  23. }
  24. ?>
  25. Download this code: /code/generate_unique_slugs_in_cakephp1.txt

Goal

What this code does, is providing a method, createSlug (), which can be accessed from all models in your application. It'll normalize the string, make it URL-friendly and last but not least, it makes it unique.

To demonstrate the "unique" part, let's say we've got a record with the title "I love CakePHP". The createSlug method will turn this into "i-love-cakePHP". Human friendly and search-engine friendly.
What happens when I wish to create two more items in my database called "I love CakePHP"? The createSlug method will generate the following two slugs: i-love-cakePHP-1 and i-love-cakePHP-2.

This way users can bookmark your URLs and always end up in the right place, even though the titles of your records may be similar.

How to use?

It's simple, really. Since the method is created in the AppModel base class, you can invoke it from every model in your application. When saving a record, you can simply call...

  1. $slug = $this->generateSlug ('my title');
  2. Download this code: /code/generate_unique_slugs_in_cakephp2.txt

...from within your models, or...

  1. $slug = $this->MyModel->generateSlug ('my title');
  2. Download this code: /code/generate_unique_slugs_in_cakephp3.txt

...from within your controllers, right before you insert new data.

Note that you have to pass the id from the current record when you're modifying existing records, so it can exclude that from its check, like this:

  1. $slug = $this->generateSlug ('my title', 10);
  2. Download this code: /code/generate_unique_slugs_in_cakephp4.txt

How does it work?

  1. $slug = Inflector::slug ($string,'-');
  2. $slug = low ($slug);
  3. Download this code: /code/generate_unique_slugs_in_cakephp5.txt

These lines normalize the string into a human friendly, easily readable slug.

  1. $params = array ();
  2. $params ['conditions']= array();
  3. $params ['conditions'][$this->name.'.slug']= $slug;
  4. if (!is_null($id)) {
  5. $params ['conditions']['not'] = array($this->name.'.id'=>$id);
  6. }
  7. Download this code: /code/generate_unique_slugs_in_cakephp6.txt

This code will create the $params array, which is used in checking wether or not a slug already exists.

  1. while (count($this->find ('all',$params))) {
  2. if (!preg_match ('/-{1}[0-9]+$/', $slug )) {
  3. $slug .= '-' . ++$i;
  4. } else {
  5. $slug = preg_replace ('/[0-9]+$/', ++$i, $slug );
  6. }
  7. $params ['conditions'][$this->name . '.slug']= $slug;
  8. }
  9. Download this code: /code/generate_unique_slugs_in_cakephp7.txt

This while loop will append the slug with an incrementing value until no results are returned from the Model::find method. This ensures the final slug string is unique.

I've used this method in multiple projects now, and have found it very useful. I hope it'll be of some use to you as well, and would like to know what you think this method can do better. Let me know in the comments!

Back to top

Filed under PHP, CakePHP

Comments:

  1. 31 December 2008 (10:37 AM) by udkl

    Useful Post.

    Will be great if you copy it into the Bakery.

  2. 17 February 2009 (07:12 AM) by sloo

    Thanks for the post. I'm a bit confused here. In your Appmodel, your function is called createSlug(). But under the section "How to use?", you called the generateSlug() function. Is this a typo and shouldn't it be the createSlug() function instead? Or am I missing something here?

    this comment has been quoted by Harmen Janssen

  3. 17 February 2009 (10:55 AM) by Harmen Janssen

    sloo wrote:

    Thanks for the post. I'm a bit confused here. In your Appmodel, your function is called createSlug(). But under the section "How to use?", you called the generateSlug() function. Is this a typo and shouldn't it be the createSlug() function instead? Or am I missing something here?

    Oops! You're right, it's a type. "createSlug" is the correct function name.

  4. 29 June 2010 (05:53 PM) by Waqas

    Hi Harmen,

    Thanks for this useful post. It is working great and has saved a lot of time. God bless you.

    Once again thanks.

    best regards,

    Waqas

  5. 13 December 2010 (06:50 AM) by huoxito

    really thanks for this post! simple and useful!

    LIke the bother said above you save me and a lot of people much time.

    thanks!

Sorry, due to spam, comments are temporarily out of order.

Back to top

Preferences

These settings will be saved for your convenience.