taggable Taggable Behavior

Note: Latest release and documentation are available from extension GitHub page.

This extension allows active record model to manage tags.

Resources

Documentation

Taggable Behavior

  1. Documentation
  2. Installation and configuration
  3. Methods
  4. Bonus features
  5. Using multiple tag groups
  6. Using taggable with CAutoComplete
  7. Change Log
  8. 1.5
  9. 1.4
  10. 1.3
  11. 1.2
  12. 1.1
  13. 1.0.2
  14. 1.0.1
  15. 1.0
  16. 0.9
  17. 0.8
  18. 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 via tagTablePk.
  • 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

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.
26 1
53 followers
1 495 downloads
Yii Version: 1.1
License: BSD-2-Clause
Category: Database
Tags: model
Developed by: samdark
Created on: Dec 8, 2009
Last updated: 11 years ago

Related Extensions