![]() |
|
sfPropelActAsTaggableBehaviorPlugin - 0.9.0Propel taggable behavior |
|
![]() |
66
users
Sign-in
to change your status |
This behavior permits to attach tags to Propel objects. It includes tag-clouds generation and helpers to display these clouds. |
| Name | Status | |
|---|---|---|
|
|
lead | gro.tocal <<ta>> reivax |
Copyright (c) 2007 Xavier Lacot
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
| Version | License | API | Released |
|---|---|---|---|
| 0.9.0beta | MIT license | 0.9.0beta | 22/12/2008 |
| Version | License | API | Released |
|---|---|---|---|
| 0.9.0beta | MIT license | 0.9.0beta | 22/12/2008 |
| 0.8.1beta | MIT license | 0.8.1beta | 22/09/2008 |
| 0.7.0beta | MIT license | 0.7.0beta | 16/07/2008 |
| 0.6.0beta | MIT license | 0.6.0beta | 10/05/2008 |
| 0.5.0beta | MIT license | 0.5.0beta | 21/03/2008 |
| Version | License | API | Released |
|---|---|---|---|
| 0.9.0beta | MIT license | 0.9.0beta | 22/12/2008 |
| 0.8.1beta | MIT license | 0.8.1beta | 22/09/2008 |
| 0.7.0beta | MIT license | 0.7.0beta | 16/07/2008 |
| 0.6.0beta | MIT license | 0.6.0beta | 10/05/2008 |
| 0.5.0beta | MIT license | 0.5.0beta | 21/03/2008 |
| 0.4.0beta | MIT license | 0.4.0beta | 11/12/2007 |
| 0.3.0beta | MIT license | 0.3.0beta | 02/07/2007 |
| 0.2.0beta | MIT license | 0.2.0beta | 27/06/2007 |
| 0.1.0beta | MIT license | 0.1.0beta | 21/05/2007 |
tag_list() helperremoveAllTags() or setTags() twice before saving an object, the tags were not removed. A big thank to Tom BoutellTagPeer::getPopulars() is now defined with app_sfPropelActAsTaggableBehaviorPlugin_limit (previously with app_tags_limit)Initial public release * add/remove tag(s) on an object * multi-tags object search * multi-models selection * tag cloud generation * related tags handling * unit-tested
This behavior permits to attach tags to Propel objects. It includes tag-clouds generation and helpers to display these clouds.

go to your project's root
Install the plugin:
./symfony plugin-install http://plugins.symfony-project.com/sfPropelActAsTaggableBehaviorPlugin
if not already done, enabled behaviors in config/propel.ini:
propel.builder.addBehaviors = true
edit the classes that you want to make taggable. For instance, for lib/model/Post.php:
<?php
class Post extends BasePost
{
}
sfPropelBehavior::add('Post', array('sfPropelActAsTaggableBehavior'));
rebuild the model:
./symfony propel-build-all
clear cache:
./symfony cc
Consider a Propel "Post" class:
<?php
class Post extends BasePost
{
}
sfPropelBehavior::add('Post', array('sfPropelActAsTaggableBehavior'));
When the sfPropelActAsTaggableBehaviorPlugin is applied to the Post class, that class automagically gets taggable. There are several ways to attach tags to an object:
<?php
$post = new Post();
$post->addTag('toto');
$post->addTag('tata, tutu');
$post->addTag(array('Titi', 'Gros Minet'));
$post->save();
Since the version 0.4, the plugin supports machine tags:
<?php
$post = new Post();
$post->addTag('iso:isbn=123456789');
$post->save();
// assume City is a taggable class
$city = new City();
$city->addTag('geo:lat=47.3456');
$city->save();
By default, the plugin will allow to attach several triple tags with the same namespaces and key for one given object. That is, you could attach the tags "geo:lat=36.5" and "geo:lat=43.2" to the same object. If this behavior doesn't make you happy, you may want to tweak the plugin's configuration in the app.yml file of your project:
all:
sfPropelActAsTaggableBehaviorPlugin:
triple_distinct: true
It is possible to retrieve tags from a taggable object:
<?php
$post = PostPeer::retrieveByPk(1);
$tags = $post->getTags();
foreach ($tags as $tag)
{
echo $tag.'<br />';
}
If you want to retrieve only the triple tags of a certain namespace, you can
pass some options to the getTags() method:
<?php
$post = PostPeer::retrieveByPk(1);
$tags = $post->getTags(array('is_triple' => true,
'namespace' => 'geo',
'return' => 'value'));
foreach ($tags as $tag)
{
echo $tag.'<br />';
}
The getTags() method may accept up to 5 parameters:
is_triple: whether or not the returned tags should be triple tags onlynamespace: namespace of the returned triple tagskey: key of the returned triple tagsvalue: value of the returned triple tagstag: complete triple-tag string of the returned triple tagsreturn: format of the returned result:
return option has the value namespace, key or value, the getTags() method will only return the namespaces, keys or values list.Of course, tags can also be removed:
<?php
$post = PostPeer::retrieveByPk(1);
$post->removeTag('toto');
$post->removeTag('toto, tutu');
$post->removeAllTags();
All the tags of an object can be set or replaced at once, using the methode
setTags():
<?php
$post = PostPeer::retrieveByPk(1);
$post->setTags('toto, tutu');
$post->save();
This is particularly useful when using File Syntax fixtures in a project, as it permits to attach tags to the objects a pretty straight way:
Post:
first_post:
title: My first memories
tags: memories, sleeping, bed
second_post:
title: Things got worse
tags: death, memories, personnal
The plugin proposes several methods for retrieving objects given their tags.
These methods are all located in the TagPeer class:
<?php
// gets the list of the models that have at least one instance tagged with one
// or several specific tags
$tutu_toto_models = TagPeer::getModelsTaggedWith('tutu, toto');
// gets objects tagged with one or several specific tags
$tutu_toto_objects = TagPeer::getTaggedWith('tutu, toto');
$tutu_toto_objects = TagPeer::getTaggedWith('tutu, toto', array('triple' => true, 'namespace' => 'geo'));
$tutu_toto_objects = TagPeer::getTaggedWith('tutu, toto', array('model' => 'Post'));
// it is als possible to select objects tagged with certain types of triple tags
// in this special case, the first "tags" parameter is useless. For instance,
// this line will return all the objects that have a triple tag in the namespace
// "geo":
$tutu_toto_objects = TagPeer::getTaggedWith(array(), array('namespace' => 'geo'));
// gets a criteria that permits to select objects tagged with one or several
// specific tags
$criteria = TagPeer::getTaggedWithCriteria('Post', 'tutu, toto');
$criteria->add(PostPeer::PUBLISHED, true);
$posts = PostPeer::doSelect($criteria);
// gets objects that are tagged with a certain number of tags within a set of
// tags. For instance, the following line returns all the object tagged with at
// least two of the following tags: toto, tutu, tata, titi
$objects = TagPeer::getTaggedWith('tutu, toto, tata, titi',
array('nb_common_tags' => 2));
The methods TagPeer::getRelatedTags(), TagPeer::getTaggedWith(), and TagPeer::getTaggedWithCriteria() accept one additional parameter, "nb_common_tags", that permits to select objects that share a certain number of tags in common with the given tags list. For instance:
<?php
// this will return all the objects that are at least tagged with 2 tags in the
// list "tata", "titi", "tutu", and "toto".
$objects = TagPeer::getTaggedWith('tata, titi, tutu, toto', array('nb_common_tags' => 2));
The plugin also proposes methods and helpers for generating tags cloud:
<?php
// gets the popular tags
$tags = TagPeer::getPopulars();
// display the tags cloud. The tags will use the route name "@tag" which tags
// the request parameter "tags". The %s element of the route represents the
// position of the tag
echo tag_cloud($tags, '@tag?tags=%s');
The default size of the tag cloud is 100 items, but this value might be tweaked in app.yml:
all:
sfPropelActAsTaggableBehaviorPlugin:
limit: 50
When you click on a tag in a tag cloud, you will want to get a list of objects that have been tagged with that tag. But sometimes, it happens that this tag is so popular that you can not find the resource you were searching for. Related-tags clouds are helpful for refining your request, as they provide a way to add an other tag to the request:
<?php
// get the tags related to "toto" and "tutu", for the model "Post" only
$tags = TagPeer::getRelatedTags('toto,tutu', array('model' => 'Post'));
// displays the related tags cloud, using the route "@post_tags" with the
// request parameter "tags". Please note that there is no %s in the route,
// on the contrary to the tag_cloud() helper
echo related_tag_cloud($tags, '@post_tags?tags=', 'toto,tutu');
This helper accepts several options:
add: text to be used, after each tag, as a link for adding this tag to the current selectionclass: class of the tags cloud. By default, the class "tags-cloud" is usedYou might also want to display the tags of one item. The tag_list() helper is done for this:
<?php
$post = PostPeer::retrieveByPk(1);
$tags = $post->getTags();
echo tag_list($tags, '@tag?tag=');
This helper accepts several options:
class: class of the tags list. By default, the class "tags-list" is usedordered: by default, the helper will generate an unordered list (HTML <ul>...</ul> tag). When this option is set to true, the helper will generate an ordered list (HTML <ol>...</ol> tag).separator: separator to be used between two tags. If this option is not added, no separator will be used.The tag retrieval mecanism is fully based on Criterias, so it is easy to pass several restrictions. For instance, for retrieving popular tags over posts created in March 2007:
<?php
$c = new Criteria();
$c->addJoin(PostPeer::ID, TaggingPeer::TAGGABLE_ID);
$c->add(PostPeer::CREATED_AT, '2007-03%', Criteria::LIKE);
$tags = TagPeer::getPopulars($c, array('model' => 'Post'));
echo tag_cloud($tags, '@tag?tags=%s');
The methods TagPeer::getPopulars, TagPeer::getAll, etc., accept as last parameter an array with several keys:
max number of returned tags:
<?php
// return a maximum of 200 tags
$tags = TagPeer::getAll(null, array('limit' => 200));
tag name restriction:
<?php
// select tags beginning with the letters "to"
$tags = TagPeer::getAll(null, array('like' => 'to%'));
whether the returned tags should be machine tags, or not:
<?php
// returns only triple tags
$triple_tags = TagPeer::getAll(null, array('triple' => true));
for triple tags, it is possible to restrict the returned tags from their namespace, key, and value:
<?php
// returns only triple tags from the namespace "geo"
$geo_tags = TagPeer::getAll(null, array('triple' => true, 'namespace' => 'geo'));
// returns only triple tags with the key "lat"
$lat_tags = TagPeer::getAll(null, array('triple' => true, 'key' => 'lat'));
// returns only triple tags with the value "12"
$value_tags = TagPeer::getAll(null, array('triple' => true, 'value' => '12'));
In case you want to display a long list of taggable objects with their associated tags, you might want first to preload these objects's tags: it avoids to load tags per object, and gets all tags in a few requests.
<?php
$posts = TagPeer::getTaggedWith('toto,tutu', array('model' => 'Post'));
sfPropelActAsTaggableBehavior::preloadTags($posts);
foreach ($posts as $post)
{
echo $post-getTitle();
// won't require one request at each loop, as tags have been preloaded.
var_dump($post-getTags());
}
The plugin associates a parameterHolder to Propel objects, with 3 disjoin namespaces:
When required, the saved_tags namespace is filled with the tags previously present in the database. The tagging methods have an action on these three namespaces, which are serialized in the database after the Propel object gets saved.
The behavior implement the following methods:
The behavior class also implement the following method, which is a facility for preloading all the tags for a set of taggable objects
The plugin has been deeply unit-tested, if not fully. The tests are located in test/unit/sfPropelActAsTaggableBehaviorTest.php. If you want to run them:
edit the test file test/unit/sfPropelActAsTaggableBehaviorTest.php and modify line 3 and 4:
define('TEST_CLASS', 'Post');
define('TEST_CLASS_2', 'Link');
run the tests:
$ php ./plugins/sfPropelActAsTaggableBehaviorPlugin/test/unit/sfPropelActAsTaggableBehaviorTest.php
if you are using sf >= 1.2, you might want to take benefit out of the test:coverage task :
$ ./symfony test:coverage plugins/sfPropelActAsTaggableBehaviorPlugin/test/unit/sfPropelActAsTaggableBehaviorTest.php plugins/sfPropelActAsTaggableBehaviorPlugin/lib/sfPropelActAsTaggableBehavior.class.php --detailed
Please note that when using the plugin with Propel 1.3, you must deactivate Propel's instance pooling in order to have all the tests pass. Else, the tags are not attached independently to various instances of the same object and, in this configuration, it is normal that all the tests do not pass.
This plugin has been developed by Xavier Lacot and is licensed under the MIT license.
tag_list() helperremoveAllTags() or setTags() twice before saving an object, the tags were not removed. A big thank to Tom BoutellTagPeer::getPopulars() is now defined with app_sfPropelActAsTaggableBehaviorPlugin_limit (previously with app_tags_limit)tag_list() helperpreloadTags() performance, in case all the objects don't have a tagsetTags() methodInitial public release. Features tags attachment to heterogene Propel objects, and includes tag-clouds generation. Thanks to Tristan Rivoallan for the help provided.

