Note: Latest release and documentation are available from extension GitHub page.
This extension allows active record model to manage tags.
Taggable Behavior
Allows active record model to manage tags.
Create a table where you want to store tags and cross-table to store tag-model connections.
You can use sample SQL from schema.sql file.
In your ActiveRecord model define behaviors() method:
function behaviors() { return array( 'tags' => array( 'class' => 'ext.yiiext.behaviors.model.taggable.ETaggableBehavior', // Table where tags are stored 'tagTable' => 'Tag', // Cross-table that stores tag-model connections. // By default it's your_model_tableTag 'tagBindingTable' => 'PostTag', // Foreign key in cross-table. // By default it's your_model_tableId 'modelTableFk' => 'post_id', // Tag table PK field 'tagTablePk' => 'id', // Tag name field 'tagTableName' => 'name', // Tag counter field // if null (default) does not write tag counts to DB 'tagTableCount' => 'count', // Tag binding table tag ID 'tagBindingTableTagId' => 'tagId', // Caching component ID. If false don't use cache. // Defaults to false. 'cacheID' => 'cache', // Save nonexisting tags. // When false, throws exception when saving nonexisting tag. 'createTagsAutomatically' => true, // Default tag selection criteria 'scope' => array( 'condition' => ' t.user_id = :user_id ', 'params' => array( ':user_id' => Yii::app()->user->id ), ), // Values to insert to tag table on adding tag 'insertValues' => array( 'user_id' => Yii::app()->user->id, ), ) ); }
For using AR model for tags (for example, to bind custom behavior), use EARTaggableBehavior.
To do it add following to your config:
return array( // ... 'import'=>array( 'application.models.*', 'application.components.*', 'ext.yiiext.behaviors.model.taggable.*', // ... // other imports ), // ... );
In your AR model implement behaviors() method:
function behaviors() { return array( 'tags_with_model' => array( 'class' => 'ext.yiiext.behaviors.model.taggable.EARTaggableBehavior', // tag table name 'tagTable' => 'Tag', // tag model class 'tagModel' => 'Tag', // ... // other options as shown above ) ); }
Replace model tags with new tags set.
$post = new Post(); $post->setTags('tag1, tag2, tag3')->save();
Add one or more tags to existing set.
$post->addTags('new1, new2')->save();
Remove tags specified (if they do exist).
$post->removeTags('new1')->save();
Remove all tags from the model.
$post->removeAllTags()->save();
Get array of model's tags.
$tags = $post->getTags(); foreach($tags as $tag){ echo $tag; }
Returns true if all tags specified are assigned to current model and false otherwise.
$post = Post::model()->findByPk(1); if($post->hasTags("yii, php")){ //… }
Get all possible tags for this model class.
$tags = Post::model()->getAllTags(); foreach($tags as $tag){ echo $tag; }
Get all possible tags with models count for each for this model class.
$tags = Post::model()->getAllTagsWithModelsCount(); foreach($tags as $tag){ echo $tag['name']." (".$tag['count'].")"; }
Limits AR query to records with all tags specified.
$posts = Post::model()->taggedWith('php, yii')->findAll(); $postCount = Post::model()->taggedWith('php, yii')->count();
could be used to reset getAllTags() or getAllTagsWithModelsCount() cache.
You can print comma separated tags following way:
$post->addTags('new1, new2')->save(); echo $post->tags->toString();
You can use multiple tag groups for a single model. For example, we will create OS and Category tag groups for Software model.
First we need to create DB tables. Two for each group:
/* Tag table */ CREATE TABLE `Os` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `Os_name` (`name`) ); /* Tag binding table */ CREATE TABLE `PostOs` ( `post_id` INT(10) UNSIGNED NOT NULL, `osId` INT(10) UNSIGNED NOT NULL, PRIMARY KEY (`post_id`,`osId`) ); /* Tag table */ CREATE TABLE `Category` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `Category_name` (`name`) ); /* Tag binding table */ CREATE TABLE `PostCategory` ( `post_id` INT(10) UNSIGNED NOT NULL, `categoryId` INT(10) UNSIGNED NOT NULL, PRIMARY KEY (`post_id`,`categoryId`) );
Then we are attaching behaviors:
return array( 'categories' => array( 'class' => 'ext.yiiext.behaviors.model.taggable.ETaggableBehavior', 'tagTable' => 'Category', 'tagBindingTable' => 'PostCategory', 'tagBindingTableTagId' => 'categoryId', ), 'os' => array( 'class' => 'ext.yiiext.behaviors.model.taggable.ETaggableBehavior', 'tagTable' => 'Os', 'tagBindingTable' => 'PostOs', 'tagBindingTableTagId' => 'osId', ), );
That's it. Now we can use it:
$soft = Software::model()->findByPk(1); // fist attached taggable behavior is used by default // so we can use short syntax instead of $soft->categories->addTag("Antivirus"): $soft->addTag("Antivirus"); $soft->os->addTag("Windows"); $soft->save();
$this->widget('CAutoComplete', array( 'name' => 'tags', 'value' => $model->tags->toString(), 'url'=>'/autocomplete/tags', //path to autocomplete URL 'multiple'=>true, 'mustMatch'=>false, 'matchCase'=>false, ))
Saving tags will look like following:
function actionUpdate(){ $model = Post::model()->findByPk($_GET['id']); if(isset($_POST['Post'])){ $model->attributes=$_POST['Post']; $model->setTags($_POST['tags']); // if you have multiple tag fields: // $model->tags1->setTags($_POST['tags1']); // $model->tags1->setTags($_POST['tags2']); if($model->save()) $this->redirect(array('index')); } $this->render('update',array( 'model'=>$model, )); }
id and can be set via tagTablePk.Warning: this version is not compatible with Yii 1.0.
Warning: this version is not backwards compatible to 0.6.
Total 18 comments
Thanks for the extension. For better flexibility would be great to have insertvalues parameter also for binding table and criteria scope too
Great extension. Thanks.
However, I have the following question. How can I get all the tags with a custom criteria? For example, if I have another taggable model or table and I want to get all tags for such model having a status = "active" condition?
Any idea how we can implement slug for tags? For example, if I want to use "link building" as a tag, then want to create the slug version "link-building".. would be that possible? I thought in using Sluggable Extension but any hint will be appreciated. Thanks!
Hello!
First of all, thanks for the extension!
I use an input field for saving tags for a model, so when I save the input field empty, I get an exception:
I have createTagsAutomatically OFF, but it should be working anyway. I use ETaggableBehavior::setTags() method and inside it calls ETaggableBehavior::toTagsArray() method which uses explode() function, so when the string argument is empty, it creates a non-empty array with one empty value. So the foreach iterates the array and there comes the exception raising.
I solved it by including a check before calling setTags()
Regards, szako
I had problems using taggable behavior with CDbCriteria. I have a class Event that uses taggable behavior. The example code shown in the description worked fine:
However using
lead to
Although I had the correct relation for "tags". After some research I found out that one has to use
in order to work correctly.
Hope this helps someone.
Hi, I'm using Taggable 1.5 and Yii 1.1.8
It seems that while using "taggedWith" method, there's some issue with clearing scope or whatever. Have a look at the following code: (note: findScopedAll is the method which returns CActiveDataProvider based on current model's criteria)
$first = News::model()->taggedWith('first'); echo $first->getDbCriteria(true)->join; echo "<hr />"; $second = News::model()->taggedWith('second'); echo $second->getDbCriteria(true)->join;The output is following: JOIN news_tags bt0 ON t.id = bt0.news_id JOIN tags tag0 ON tag0.id = bt0.tag_id AND tag0.
name= 'first'0JOIN news_tags bt0 ON t.id = bt0.news_id JOIN tags tag0 ON tag0.id = bt0.tag_id AND tag0.
name= 'first' JOIN news_tags bt0 ON t.id = bt0.news_id JOIN tags tag0 ON tag0.id = bt0.tag_id AND tag0.name= 'second'as you can see, the second is invalid - it's simply a merge of previous and new one.
calling resetScope() right after second initialization fixes the problem, but I don't think it is expected way to go
especially combined with tag-it :)
You put files to yiiext\behaviors\model\taggable\yiiext\behaviors\model\taggable in taggable_1.4.zip
Too much recursion, fix it :)
I commented the two existing lines for jquery-ui:
And added:
Thanks for the extension!
Trying to implement this extension and I am getting a CException error message to my page.
Otherwise, getTags seems to be working just fine so I think I've got the extension configured correctly, except for this:).
Any advice on overcoming this is appreciated. Thank you.
Hello, nice extension. I've got a bug report/fix: If you use Taggable to filter out records using multiple tag groups and write something like this:
You'll get CDbException:
My quick fix for this is to alter ETaggableBehavior::getFindByTagsCriteria() like this:
samdark, this extension is perfect! :) I only want to add that in some parts (i.e. some headers) you added some Russian (Cyrillic) letters, like "или" which probably should stand for "and". Am I right? :)
Slight documentation error,
EARTaggableBehaviour should be EARTaggableBehavior
otherwise you'll get an import error
This problem has nothing to do with the Taggable extension. I feel bad having left that comment now. Basically the column wasn't set to 'auto_increment' so it was my fault. This extension is amazing! Thanks for the contribution to the Yii community!
I am running into the same problem as Mech7. I have this addition coupled with the Tag-widget and it looks great... only problem is when I try to save the tags I get a duplicate key error. Can you help us clear this up?
When i use it in the aftersave i always get:
CDbCommand failed to execute the SQL statement: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '11' for key 'PRIMARY'
In your function getAllTags(), you always overwrite the $criteria argument. It's impossible that way to use it for autocomplete.
Very usefull extesion. Please change private functions to protected type, that they could be redefined by inheritors.
Leave a comment
Please login to leave your comment.