Yii 1.1: yiibehaviorsluggable

Make a human readable permalink URL for your model
45 followers

What's it all about?

With this behavior, you can generate an URI for a single or combination of columns in your table. Some call it permalink, others call it slug or human readable url.

Check out the latest version at: github.com/mintao/yii-behavior-sluggable

Imagine a blog table

| id | category | title                             |
|----+----------+-----------------------------------|
|  1 | security | NASA Server hacked by hacker kids |
| ...

So you'd like a better URL than that?

http://your-blog.org/index.php?r=blog/show&id=1422

What about

http://your-blog.org/security/nasa-server-hacked-by-hacker-kids

Google will love you ;)

How to get it done

  • Add another column called "slug" to your table
  • Download this extension and drop it into your protected/extensions folder,
  • Add the behavior to your model (shown below)

If you're using git, I'd recommend:

cd <YOUR YII-PROJECT>
mkdir -p protected/extensions/behaviors (optional)
git submodule add git://github.com/mintao/yii-behavior-sluggable.git protected/extensions/behaviors/SluggableBehavior

Demo configuration of this behavior for your model

/**
 * Behaviors for this model
 */
public function behaviors(){
  return array(
    'sluggable' => array(
      'class'=>'ext.behaviors.SluggableBehavior.SluggableBehavior',
      'columns' => array('category', 'title'),
      'unique' => true,
      'update' => true,
    ),
  );
}
  • class Defines the path where to find the SluggableBehavor.php
  • columns Needs to be an array of the fields to use for slug creation
  • unique Set this to true to ensure a unique slug (Numbers will be added to duplicate slugs, if already existing)
  • update Set this to true is every time you change the entry, the slug should be updated too. Set it to false, if the slug should be only created once

Download this extension (and fork it):

github.com/mintao/yii-behavior-sluggable

Changelog

  • 2011-04-30 Added update functionality

Total 20 comments

#8777 report it
Tiff at 2012/06/26 08:58am
missing something?

if I use 'columns' => array('ProductType.slug', 'name'),

I don't get a separator / between the product type and the product, it just all flows on with hyphens?

#8755 report it
Luciano at 2012/06/24 11:56pm
Words to exclude

Sorry if I missing it, but it would be nice having something to exclude words from being slugged.

Something like

/**
 * Behaviors for this model
 */
public function behaviors(){
  return array(
    'sluggable' => array(
      'class'=>'ext.behaviors.SluggableBehavior.SluggableBehavior',
      'columns' => array('category', 'title'),
      'unique' => true,
      'update' => true,
      'exclude' => array('a', 'an', 'and', 'of'......),
    ),
  );
}

Anyway, loving this extension... :)

#7388 report it
bas_vdl at 2012/03/19 05:57pm
- instead of / between two columns

my sludge looks like: noord-holland-s-graveland. where noord-holland is the province and s-graveland the city. but should it not be like: noord-holland/s-graveland

so how do i have to configur this extension to get i get: noord-holland/s-graveland

#4777 report it
jwinn at 2011/08/15 02:12am
Issue and a Quickfix

Awesome, thanks for this.

Problem with urlManager I am having a problem with getting 'The system is unable to find the requested action' when adding the urlManager rule. The page loads correctly with the query string.

'mycontroller/<slug:[\w\_]+>'=>'mycontroller/view',
'<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',

EDIT: Figured it out thanks to this post. The problem was with the regular expression \w not allowing the dash. I changed the rule to the following.

'hotsauce/<slug:[a-zA-Z0-9-]+>'=>'mycontroller/view'

Single Quotes / Apostrophes The slug that is generated was putting dashes in place of apostrophes/single quotes. For example, "person's foobar" would turn into "person-s-foobar". So I changed the urlize function in Doctrine_Inflector.php to remove them before the other preg_replaces are done.

I added the following before "//Remove all none word characters"

// Remove all single quotes/apostrophes
$text = str_replace(array("'", ""), "", $text);
#4687 report it
bas_vdl at 2011/08/04 06:04am
Great ext, but i was wondering...

my sludge looks like: noord-holland-s-graveland. where noord-holland is the province and s-graveland the city. but should it not be like: noord-holland/s-graveland

in the example it states: http://.../security/nasa-server-hacked-by-hacker-kids

so how i get an url like: http://.../noord-holland/s-graveland

#4316 report it
Joe Storm at 2011/06/24 11:20pm
count number not consistent

and the count suffix added to the slug is not quite consistent

I created a few articles of the same title and edit the slug fields randomly by removing the "-1" or "-2".. and deleting some articles and add again..

the slugs generated sometimes shows duplicate slugs like

1 "test-slugs"

2 "test-slugs"

3 "test-slugs-2"

or

1 "test-slugs"

2 "test-slugs-2-1"

It doesn't checks the rows properly or assign the right numbers although it will work well with 1 or or 2 records without much editing..

I am new to slug thing..is this how it is meant to be? or do you have plans to further update this extension?

Thanks! great work anyhow...!

#4315 report it
Joe Storm at 2011/06/24 11:06pm
duplicate entry

Hi mintao, thanks for extension

but can you make it detect duplicate slugs?

currently if I create 2 articles with the same title ("Test Slug") it will generate 2 slugs respectively ("test-slug" and "test-slug-1")

when I edit the artice of "test-slug-1" and change it to "test-slug" it will be accepted and thus there will be 2 "test-slug" in the db

I want to make the Slug field editable in case the user wants to customize it.

how is this handled?

Thanks!

#4112 report it
abhimir at 2011/06/07 11:10am
@mintao

Had to make a few changes, but got it working. Thank you very much for the help. I will also take this opportunity to congratulate you on this excellent extension. Keep up the good work.

#4111 report it
mintao at 2011/06/07 09:45am
Url with slug

If you want to use the slug, set a link like this (Not verified, just hacked into this textarea ;)) Hope you find your way ...

// In your view book/list.php
echo CHtml::link('Details for book ' . $book->name, array('/book/show', 'slug'=>$book->slug);
 
// In your config/main.php
array(
    ......
    'components'=>array(
        ......
        'urlManager'=>array(
            'urlFormat'=>'path',
            'showScriptName'=>false,
            'rules'=>array(
                ...........
                'book/<slug:[\w\_]+>'=>'book/show',
            ),
        ),
    ),
);
// See more info at http://www.yiiframework.com/doc/guide/1.1/en/topics.url
 
// In your controller/BookController.php
class BookController extends Controller
{
  .....
  public function actionShow($slug) {
    $book = Book::model()->findByAttributes(array('slug'=>$slug));
    $this->render('show',array('book'=>$book));
  }
}
#4105 report it
abhimir at 2011/06/06 07:10am
/config/main.php urlManager rules

The extension is working for me. The slugs are created and stored in DB.

Now what changes should I make to the urlManager rules to get the slug displayed instead of the id?

I am trying to use this with a Books model (hence tbl_books), with the slug containing only the contents of the column "title". So instead of the url showing

    /books/1

I want it to show

    /books/(content of the slug field)

I now this is trivial, but being a Yii newbie I do not know how to set urlManager rules to do this.

Thanks for your help.

#3896 report it
PeRoChAk at 2011/05/18 01:15pm
Reply to comment #3893

Yep

You need to first learn Yii::app()->createUrl

#3893 report it
MichaelH at 2011/05/18 08:39am
great extension

Had one little problem with the extension

When i pull my model from a session i get the following error:

Fatal Error: CComponent::__call() [ccomponent.--call]: The script tried to execute a method or access a property of an incomplete object. Please ensure that the class definition "SluggableBehavior" of the object you are trying to operate on was loaded before unserialize() gets called or provide a __autoload() function to load the class definition in E:\Websites\yii\framework\base\CComponent.php on line 260

I solved this by adding the following line on top of my model:

Yii::import('ext.behaviors.SluggableBehavior.SluggableBehavior');

When i use Yii::app()->controller->createUrl('page', $model->slug); it create the URL like this:

http://your-blog.org/security-nasa-server-hacked-by-hacker-kids

and not: http://your-blog.org/security/nasa-server-hacked-by-hacker-kids

Did i approach this wrong?

#3799 report it
PeRoChAk at 2011/05/08 04:07pm
Reply to comment #3666

In main.php in UrlManager

you can use

'/security/<topic:.*?>'=>'post/view',
#3666 report it
Pravin at 2011/04/27 02:47am
Url replacement!

@mintao

Thanks for the reply. As I mentioned earlier, I have got the slugs created and stored in the db and also can access it using $model->slug.

I want to know what to be configured in the url manager in config.php so that it will replace a particular controller/action/id with the 'slug' that has just been created. (What url rules are to be added?)

#3651 report it
mintao at 2011/04/26 09:48am
Missing explaination

@ppravin88

  1. In your blogpost model, assign the behavior
  2. In the behavior define the db fields e.g. your url should contain the title of your post simply use 'columns' => array('title'),
  3. After saving a new post, the URL is automatically created and can be accessed by $post->slug (where $post is your post model, and slug your additional field in the db to store the urls for your posts)
#3650 report it
Pravin at 2011/04/26 09:38am
Hiding the ids!

@iota and @all

Can i get the url to be in the same form as in the example:

http://your-blog.org/index.php?r=blog/show&id=1422

... so what about ...

http://your-blog.org/security/nasa-server-hacked-by-hacker-kids

ie., the url should be only text( even the id should be hidden)

I got certain options of changing a particular controller/action to a particular text, but the id that comes after that was still visible. How get a url as mentioned in the example?

Thanks!

#3560 report it
iota at 2011/04/19 05:04am
Creating URLs from slugs

@ppravin88

I found that the following extension works in well with SluggableBehavior to create your Urls:

Hope that helps :)

#3556 report it
Pravin at 2011/04/19 03:41am
Help..!

hi there.. i have this problem..! slugs are created properly and stored in the db.. but how to replace the url with the slug created..?

#3507 report it
briiC.lv at 2011/04/16 05:14am
javascript alternative

If someone (like me) searching for javascript make me slug alternative, you could use javascript code below:

// (c) http://dense13.com/blog/2009/05/03/converting-string-to-slug-javascript/
function string_to_slug(str) {
  str = str.replace(/^\s+|\s+$/g, ''); // trim
  str = str.toLowerCase();
 
  // remove accents, swap ñ for n, etc
  var from = "àáäâèéëêìíïîòóöôùúüûñç·/_,:;";
  var to   = "aaaaeeeeiiiioooouuuunc------";
  for (var i=0, l=from.length ; i<l ; i++) {
    str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
  }
 
  str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars
    .replace(/\s+/g, '-') // collapse whitespace and replace by -
    .replace(/-+/g, '-'); // collapse dashes
 
  return str;
}

Add some your characters wich you want to replace to "from" string and "to" string. (i added latvian "ā" to replace it with simple "a")

var from = "āàáäâèéëêìíïîòóöôùúüûñç·/_,:;";
  var to = "aaaaaeeeeiiiioooouuuunc------";

And now use it on title input keyup (jquery example) like

$(function(){
   $inTitle = $("#title"); //user are writing here
   $inSlug = $("#slug"); //javascript autogenerated slug comes here
   $inTitle.keyup(function(){
      $inSlug.val($inTitle.val());
   });
});

and if you want to prefix it with your url

...
      $inSlug.val('http://my-domain.com/post/'+$inTitle.val());
...
  • slug input are still editable for your changes. Now final step is to edit your config/main.php for correct routes.
#3504 report it
Dudie Rirkx at 2011/04/15 08:33pm
beautiful

Excellent idea, excellent practice, excellent tutorial.

"Google will love you ;)"

I love it!

Leave a comment

Please to leave your comment.

Create extension
Downloads
No downloadable files yet