tags:

views:

62

answers:

5

its when i starting trying implementing this that i got quite stuck.

some business rules

1 Post can have many Tags. 1 Tag can have many Posts

the database will look like.

  • Posts (id, title, body, ...)
  • Posts_Tags (post, tag)
  • Tags (id, tag, ...)

when i insert - straightforward

  • tags will come from user input as comma separated values
  • explode($tags) to get individual tags
  • foreach $tag
    • check if tag exists
    • if yes, get id
    • if no, insert tag & get id
  • insert post with tags

i am wondering if this is the best way? can i do away with the loop to chk if the tags exists? or simplify it into 1 query?

update a post, abit harder

  • how can i check if the user has updated any tags. another loop? but this time there will be some changes (italic)

  • explode($tags) to get individual tags

  • foreach $tag
    • check if this post has been tagged with this tag
    • if no,
      • does tag exists?
        • yes, get id
        • no, insert and get id
    • if yes, get id
  • update post with tags

hmm, ... update is more confusing, how will u implement this?

i am using PHP 5.3, Zend Framework 1.10, Doctrine 2

+1  A: 

An idea for update:

  • Store the value for user JS enabled/disabled in a hidden value
  • Tie an onchange event to the tags input then set a hidden variable to true so you know tags has changed
  • If JS enabled and tags has changed delete all tags belongs to this post then insert the actual tags (like first)
    • Fallback: if JS disabled you need to check submitted tags against saved ones.
fabrik
this will be a more efficient method assuming user enabled js.
jiewmeng
You'd still have to do the processing on the PHP side though, because you can't guarantee that JS is enabled.
Daniel Vandersluis
+1  A: 

I do tags pretty similar to how your updated psuedocode is, except for one added step. You need to check to make sure no tags exist for the post that do not are not in the user input. For example if a post is tagged "php,sql,doctrine" and the user changes the tag to "c#,sql,doctrine" you have to know to delete the php tag as well.

So my code loops through existing tags to see which tags need to be removed, then I loop through the user supplied tags to see which need to be added.

KallDrexx
+2  A: 

I think the post-tags relationship should be many-to-many; ie: posts have and belong to many tags. This can be implemented with a junction table. Here's some literature on the subject. Then...

Insert: Do the same thing you are doing now, except you can fetch all tags in one query :

SELECT name FROM tags WHERE name IN ($tags)

Update: The easy way out: Remove all current tags, insert tags as if they were all new. Or...

$tags = explode(',' $_POST['tags']); // remove whitespaces

# delete all current tags that were not included in the edit
DELETE FROM post_tags WHERE post_id=$post_id AND tag_id NOT IN ($tags)

# find out what the current tags are, use a JOIN to find out their labels
SELECT tag_id FROM post_tags WHERE post_id=$post_id
fetch results, remove found tags from $tags, then
foreach new $tag, insert as if they were new
NullUserException
u are right, updated my post. remove and inset all tags wont degrade performance too much i guess?
jiewmeng
I never liked the many to many tagging schemas. Seemed inefficient to have to join the 3 tables together instead of 2 just to get the tag list, especially since the tag and post_tag tables can get pretty big depending on how active the site is.
KallDrexx
@KallDrexx that is unfortunately seems to be the [best way](http://stackoverflow.com/questions/3152850/best-way-to-store-a-many-to-many-relationship-in-mysql) to do this
NullUserException
@KallDrexx, true, but i think i will also limit 1 post to have a max of say 5 tags like here on SO?
jiewmeng
@NullUserException - Why is it better than using One to Many though? I know it's more efficient space wise and more normalized, but is it better in regards to performance to do it M2M rather than O2M?
KallDrexx
@jiewmeng - It doesn't really matter which method you use if you are limiting tag count really. However, SO stores their tags in the posts table (or whatever table a question is called, I forgot) as a delimited string. No joins required. See http://sqlserverpedia.com/wiki/Understanding_the_StackOverflow_Database_Schema
KallDrexx
@KallDrexx Suppose you store tags as a list in the posts table. What do I do if I want to find all posts tagged with a particular tag?
NullUserException
If you were asking if that's how I do my tagging then no, I have a post table and a post_Tags table, with a 1->* relationship between them. post_tags has a post_id field and a tag field, which is just a varchar. I then essentially do `select post_id from post_tags where tag = @tag` with my ORM. If you were asking how I would search tags if I did it the SO method I have no idea actually. I guess you would have to do something like `select * from posts where tag_list like '%@tag%'`
KallDrexx
@KallDrexx SO actually also has a post_tags type of table; it is just denomarlized for performance
NullUserException
i wonder if i can do this without a loop if possible in doctrine 2?
jiewmeng
A: 

For your first question, you can simplify things into a single query. The (non-parameterized) SQL would look something like this:

SELECT * FROM Tags WHERE name='foo' OR name='bar' OR name='etc'
/* and so on and so forth */

You can then cycle through in your PHP code to check which tags were returned; I'd use key_exists("keyname", $associativeArrayContainingQueryResults).

I'd do something similar for the second part of your query, too. I have no idea what the performance benefit is, though. SQL might cache the results from several smaller queries better, but I don't know if it would be faster overall.

James
hmm i think i will do an `IN()`: `SELECT * FROM Tags WHERE name IN ('foo', 'bar', 'baz')`
jiewmeng
That would be easier to generate. :-)
James
why do u say so? it seems like to me instead of generating `name = 'foo'` i generate just `'foo'` ltr implode them using `', '` as glue and append it to the `WHERE` clause?
jiewmeng
Yeah, I guess it's not actually any easier. Short code makes everything look easy.
James
A: 

I think there is a Taggable behaviour in Doctrine. Have a look at this, this can help.

GiDo
hmm if i am not wrong doctrine 2 does not support behaviours?
jiewmeng
oh, it's possible. I know there is something like this in doctrine 1.2 but you are right: http://www.doctrine-project.org/blog/doctrine2-behaviours-nutshell
GiDo