Note: Latest release and documentation are available from extension GitHub page.
This extension allows active record model to manage tags.
Resources ¶
Documentation ¶
Taggable Behavior ¶
- Documentation
- Installation and configuration
- Methods
- Bonus features
- Using multiple tag groups
- Using taggable with CAutoComplete
- Change Log
- 1.5
- 1.4
- 1.3
- 1.2
- 1.1
- 1.0.2
- 1.0.1
- 1.0
- 0.9
- 0.8
- 0.6
Allows active record model to manage tags.
Installation and configuration ¶
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
)
);
}
Methods ¶
setTags($tags) ¶
Replace model tags with new tags set.
$post = new Post();
$post->setTags('tag1, tag2, tag3')->save();
addTags($tags) or addTag($tags) ¶
Add one or more tags to existing set.
$post->addTags('new1, new2')->save();
removeTags($tags) or removeTag($tags) ¶
Remove tags specified (if they do exist).
$post->removeTags('new1')->save();
removeAllTags() ¶
Remove all tags from the model.
$post->removeAllTags()->save();
getTags() ¶
Get array of model's tags.
$tags = $post->getTags();
foreach($tags as $tag){
echo $tag;
}
hasTag($tags) или hasTags($tags) ¶
Returns true if all tags specified are assigned to current model and false otherwise.
$post = Post::model()->findByPk(1);
if($post->hasTags("yii, php")){
//…
}
getAllTags() ¶
Get all possible tags for this model class.
$tags = Post::model()->getAllTags();
foreach($tags as $tag){
echo $tag;
}
getAllTagsWithModelsCount() ¶
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'].")";
}
taggedWith($tags) или withTags($tags) ¶
Limits AR query to records with all tags specified.
$posts = Post::model()->taggedWith('php, yii')->findAll();
$postCount = Post::model()->taggedWith('php, yii')->count();
resetAllTagsCache() and resetAllTagsWithModelsCountCache() ¶
could be used to reset getAllTags() or getAllTagsWithModelsCount() cache.
Bonus features ¶
You can print comma separated tags following way:
$post->addTags('new1, new2')->save();
echo $post->tags->toString();
Using multiple tag groups ¶
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:
[sql]
/* 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();
Using taggable with CAutoComplete ¶
<?$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,
));
}
Change Log ¶
1.5 ¶
- updateCount now uses proper PK name (RSol)
1.4 ¶
- Change CacheID defaults to false. If false don't use cache.
- Fixed phpDoc
- Fixed cache key generation (Sam Dark)
- Fixed English docs (Sam Dark)
27: Fixed typos (Sam Dark) ¶
1.3 ¶
- Tag table primary key is no longer hardcoded as
id
and can be set viatagTablePk
. - Additional values inserting support (mitallast).
- Default scope criteria support (mitallast).
- Better criteria support (mitallast).
- Files and classnames are renamed to ETaggableBehavior and EARTaggableBehavior.
- Added criteria support to ETagListWidget.
- More flexibility for ETagListWidget url.
1.2 ¶
- Fixed getting tags array from string with separator at the end or beginning of line.
- Fixed getting wrong ids when using withTags() or taggedWith().
- ETagListWidget.
- Added getTagsWithModelsCount().
- getAllTagsWithModelsCount now can accept criteria.
- Input is being passed through strip_tags now.
1.1 ¶
- DBConnection is now saved in a private variable for better perfomance.
- Added tagTableName property that allows customizing name field http://code.google.com/p/yiiext/issues/detail?id=12
- Added tagTableCount property specifying counter field for storing tag count in database http://code.google.com/p/yiiext/issues/detail?id=17
- New subclass EARTaggableBehaviour for using behavior along with Tags model http://code.google.com/p/yiiext/issues/detail?id=13
1.0.2 ¶
- New manual section: using taggable with CAutoComplete.
- Renamed __toString to toString since magic was hard to debug in case of failure.
- Fixed more possible cache keys overlap when using multiple tag sets within one model.
1.0.1 ¶
- New naming conventions.
1.0 ¶
- More PHPDoc documentation.
- Fixed possible cache keys overlap when using multiple tag sets with one model.
0.9 ¶
Warning: this version is not compatible with Yii 1.0.
- Added resetAllTagsCache() and resetAllTagsWithModelsCountCache().
- Fixed getAllTags() and getAllTagsWithModelsCount() cache issues.
- Now tags are saved on save() only if they were changed.
- Extension is now compatible only with Yii 1.1.
- Fixed saving empty tags.
- Fixed caching.
0.8 ¶
Warning: this version is not backwards compatible to 0.6.
- Now you can set tagId field name for binding table.
- Do not try to delete tag bindings when inserting new record.
- Added taggedWith() alias withTags().
- Removed getCountByTags(), findAllByTags(). Use taggedWith().
- Method chaining: $post->addTags("yii, php")->save();
- New syntax: $posts = Post::model()->taggedWith(array('php', 'yii'))->findAll();
- Added parent:: calls in event handlers.
- Added hasTags() and it's alias hasTag() to check if all tags specified are attached to current model.
- New syntax: echo $post->tags (or by what name behaviour is attached) will print comma separated tags.
- getTags now returns array since implode is really easy to do yourself.
- Removed getTagsArray().
- addTags(), removeTags(), setTags() are now accept both string and array.
- Added addTag() as alias of addTags(), removeTag() as alias of removeTags().
- Some methods are now protected.
- Added $with to findAllByTags().
- getAllTags().
- Unit tests.
- createTagsAutomatically option allows to throw exception when adding nonexisting tag.
0.6 ¶
- Initial public release.
Great extension
Slight documentation error,
EARTaggableBehaviour should be EARTaggableBehavior
otherwise you'll get an import error
Figured out the 'duplicate key' problem
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!
Great addition to any site but receiving duplicate key error
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?
Error
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'
Excellent but small bug
In your function getAllTags(), you always overwrite the $criteria argument.
It's impossible that way to use it for autocomplete.
Thanks!
Very usefull extesion. Please change private functions to protected type, that they could be redefined by inheritors.
Some Russian addings! :)
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? :)
getFindByTagsCriteria() bug
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:
$baseNodeFinder = Post::model()->ongoing(); if($_GET['tag']) { $baseNodeFinder->taggedWith($_GET['tag']); } if($_GET['category_id']) { $baseNodeFinder->boundCategories->taggedWith($_GET['category_id']); } $results = $baseNodeFinder->findAll();
You'll get CDbException:
> Syntax error or access violation: 1066 Not unique table/alias: 'bt0'
My quick fix for this is to alter ETaggableBehavior::getFindByTagsCriteria() like this:
/** * Get criteria to limit query by tags * * @access private * @param array $tags * @return CDbCriteria */ protected function getFindByTagsCriteria($tags) { $criteria = new CDbCriteria(); $iFix = $this->getIFix(); $pk = $this->getOwner()->tableSchema->primaryKey; if(!empty($tags)){ $conn = $this->getConnection(); $criteria->select = 't.*'; for($i = 0, $count = count($tags); $i < $count; $i++){ $tag = $conn->quoteValue($tags[$i]); $criteria->join .= "JOIN {$this->getTagBindingTableName()} bt$i$iFix ON t.{$pk} = bt$i$iFix.{$this->getModelTableFkName()} JOIN {$this->tagTable} tag$i$iFix ON tag$i$iFix.{$this->tagTablePk} = bt$i$iFix.{$this->tagBindingTableTagId} AND tag$i$iFix.`{$this->tagTableName}` = $tag"; } } if($this->getScopeCriteria()){ $criteria->mergeWith($this->getScopeCriteria()); } return $criteria; } /** * Fix for multiple tag groups search * @author b3atb0x */ protected function getIFix() { static $iFixLog; $i = mt_rand(1,99999999); if(in_array($i, $iFixLog)) { return $this->getIFix(); } else { $iFixLog[] = $i; return $i; } }
jquery-ui-min.js ... does not exist
Trying to implement this extension and I am getting a CException error message to my page.
The asset "...\framework\zii\vendors\jui/js/jquery-ui.min.js" to be published does not exist.
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.
follow up on "jquery-ui-min.js ... does not exist"
I commented the two existing lines for jquery-ui:
$jui=Yii::app()->getAssetManager()->publish(Yii::getPathOfAlias('zii.vendors.jui').'/js/jquery-ui.min.js'); $cs->registerScriptFile($jui);
And added:
$cs->registerCoreScript('jquery-ui');
Thanks for the extension!
Fail
You put files to yiiext\behaviors\model\taggable\yiiext\behaviors\model\taggable in _taggable1.4.zip
Too much recursion, fix it :)
seems to be great
especially combined with tag-it :)
Possible serious bug while building Criteria
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
Using taggable with CDbCriteria
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:
Event::model()->taggedWith('MyTag')->find()
However using
$criteria = new CDbCritera(); $criteria->with = array('tags'); $criteria->addSearchCondition('tags.name', 'MyTag');
lead to
> CDbCommand failed to execute the SQL statement: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'tags.name' in 'where clause'.
Although I had the correct relation for "tags". After some research I found out that one has to use
$criteria->together = true;
in order to work correctly.
Hope this helps someone.
Exception when saving empty list
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:
throw new Exception("Tag \"$tag\" does not exist. Please add it before assigning or enable createTagsAutomatically.");
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()
if (empty($_POST['devicetags'])) { $tags = array(); } else { $tags = $_POST['devicetags']; } $model->tags->setTags($tags);
Regards,
szako
Slug
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!
Getting all tags where binded table has a status = "active"
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?
InsertValues
Thanks for the extension.
For better flexibility would be great to have
insertvalues parameter also for binding table and criteria scope too
Thanks
Fits like a glove to my project!
passing values dynamically
Thanks for great extension.
How can I pass values dynamically by using insertValues?
Or another way; how can I pass the model attributes value to behavior?
My tag table has additional columns (such as sef_url, meta_description etc) and I need to set them automatically.
compte le nombre de post dans une catégorie
Bonjour a vous je suis nouveau utilisateur de yii je veux un compteur le nombre de post dans une catégorie.Est possible avec cet extension? mon site est une application de petit annonce.
Merci j'attend votre reponse
compte le nombre de post dans une catégorie
Bonjour a vous je suis nouveau utilisateur de yii je veux un compteur le nombre de post dans une catégorie.Est possible avec cet extension? mon site est une application de petit annonce.
Merci j'attend votre réponse
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.