sfPropelActAsTaggableBehavior user_id enhancement

I love symfony (aka the PHP Symfony Framework) for the structure it brings to application development. Finally all the patterns known from e.g. the JAVA world are accessible (and required) in a PHP environment. I presume, this will make future web applications based on it a lot more stable, error prone and enhanceable.

Another aspect is the speed of the development once you got used to it and know the basics. But then there is always the point you stumble with your knowledge and can’t really proceed. This happened to me while implementing the faboulous sfPropelActAsTaggableBehavior plugin. It adds with a bit of configuration the ability to have tags for all your propel based objects. The documentation provided is great and functionality out of the box close to complete. Close only though.

The Requirement

I have an application I use since symfony 1.0 to get to know the latest framework updates. I gonna get it out for production use soon and wanted to add some last gimmicks. The application allows you to get nutrient informations for food and to track it in a personal diary. I thought adding tags would be very beneficial as it allows users to easily group it as required. And this could be done for foods as well as receipes (though not yet implemented), I checked out the sfPropelActAsTaggableBehaviour plugin. And it is all I needed, but the fact that I don’t want any user to remove tags that are not his own.

The Solution

I figured the best way to accomplish what I needed is to enhance the sf_tagging table in the schema with a reference to my sf_guard_user as follows:

sf_tagging:
_attributes:      { phpName: Tagging, package: plugins.sfPropelActAsTaggableBehaviorPlugin.lib.model }
id:               { phpName: ID, type: integer, required: true, primaryKey: true, autoincrement: true }
tag_id:           { type: integer, required: true, foreignTable: sf_tag, foreignReference: id, onDelete: CASCADE }
taggable_model:   varchar(30)
taggable_id:      integer
user_id:          { type: integer, default: null, required: false, foreignTable: sf_guard_user, foreignReference: id, onDelete: SETNULL }
_indexes:
tag:            [tag_id]
taggable:       [taggable_model, taggable_id]

So far so good. But than I stumbled implementing it into the plugin code. I also checked the forums and couldn’t find any help there. Sending an email to the developer was not of success either, which I can understand as answering every bloody request from noobs and intermediates would get quite time consuming. What’s left? Bloody learn the trick. So read the chapter about mixins in the symfony book (Chapter 17: Extending Symfony) and an a tutorial that explains the development of the the Paranoid Behavior.

That helped me to understand I was looking in the wrong places as the configured model classes (for Food and Receipe) were enhanced with hooks that execute the code in the sfPropelActAsTaggableBehavior.class.php.  In here it is the method public function postSave(BaseObject $object) I need to enhance. In the first foreach loop I added the following code:

// save new tags
foreach ($tags as $tagname)
{
$tag = TagPeer::retrieveOrCreateByTagName($tagname);
$tag->save();
$tagging = new Tagging();
$tagging->setTagId($tag->getId());
$tagging->setTaggableId($object->getPrimaryKey());
$tagging->setTaggableModel(get_class($object));
if(sfContext::getInstance()->getUser()->isAuthenticated())
$tagging->setUserId(sfContext::getInstance()->getUser()->getGuardUser()->getId());
else
$tagging->setUserId(null);
$tagging->save();

}

And yeah, it works flawlessly. 🙂

What’s left

Working on the behavior class directly is not a good idea, so I will create an inherited class that enhances the postSave method rather than changing it directly. And than there is the logic to be added for removing the tag. It will simply check whether the saved user_id is the same as the id of the current user. That’s it.

I’m quite happy with my little learning experience as the whole concept of mixins and behaviors did not cross my road before. But it is so useful, that I probably enhance some of my plugins (e.g. for images) into behaviors. Nice exercise that will be. And I can only encourage anyone to stick his nose into those slightly more advanced topics as they will benefit you in many ways.

And for the nutrition site, the next problem to be solved will be finding the best way to implement a Contend Delivery Network for better response times of the Frontend.

Advertisements

2 Responses

  1. any new update on this one? I find it odd the standard plugin wouldn’t do this. Social tagging is everywhere.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

%d bloggers like this: