What's it all about? ¶
- Imagine a blog table
- So you'd like a better URL than that?
- What about
- Demo configuration of this behavior for your model
- Download this extension (and fork it):
- Changelog
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
beautiful
Excellent idea, excellent practice, excellent tutorial.
"Google will love you ;)"
I love it!
javascript alternative
If someone (like me) searching for javascript make me slug alternative, you could use javascript code below:
[javascript] // (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")
[javascript] var from = "āàáäâèéëêìíïîòóöôùúüûñç·/_,:;"; var to = "aaaaaeeeeiiiioooouuuunc------";
And now use it on title input keyup (jquery example) like
~~~
[javascript]
$(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
[javascript] ... $inSlug.val('http://my-domain.com/post/'+$inTitle.val()); ...
Now final step is to edit your config/main.php for correct routes.
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..?
Creating URLs from slugs
@ppravin88
I found that the following extension works in well with SluggableBehavior to create your Urls:
Hope that helps :)
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!
Missing explaination
@ppravin88
e.g. your url should contain the title of your post
simply use
'columns' => array('title'),
by $post->slug (where $post is your post model, and slug your additional
field in the db to store the urls for your posts)
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?)
Reply to comment #3666
In main.php in UrlManager
you can use
'/security/<topic:.*?>'=>'post/view',
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?
Reply to comment #3893
Yep
You need to first learn Yii::app()->createUrl
/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
I want it to show
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.
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)); } }
@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.
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!
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...!
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
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);
- 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
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... :)
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?
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.