MODX contains two Template Variable types aimed at tagging: Tag and Auto-Tag. The latter has a more elaborate interface, but the basic functionality of the TV's are the same. Both TV types are simple text fields, allowing you to enter a number of comma separated tags. Before the TV is stored in the database, all spaces surrounding the comma's are removed. When the TV is read by a snippet like tagLister, all tags are presented as separate links. However, when clicking one of those links, you are likely to get more than you'd hoped for.
THE PROBLEM
Let's suppose we have a number of pages each containing one or more tags, like so:
- Page 1 - box
- Page 2 - box,container
- Page 3 - cardboard box
- Page 4 - boxing
When using tagLister to summarize all available tags it will create the following list (number of hits between parentheses):
box (2),
container (1),
cardboard box (1),
boxing (1)
Every tag is treated as a separate entity, as you'd expect. When you click on 'box', using getResourcesTag, you will get a list containing the following documents:
- Page 1 - box
- Page 2 - box,container
- Page 3 - cardboard box
- Page 4 - boxing
Huh? TagLister indicated there were only 2 documents using the tag 'box', but getResourceTag shows 4. How come? Well, GetResourcesTag searches the TV's looking for the following SQL query:
This means it will see a match whenever the word 'box' shows up, whether or not it is surrounded by other characters.
We decide to use getResources to get a bit more control and look for the query:
This yields the following result:
Snippets can break a Tag TV up into separate tags, but when searching the database you don't have that luxury. In that case tags are treated as what they really are: a single line of comma delimited text. So as a workaround we could search for:
LIKE box||LIKE %,box,%||LIKE %,box||LIKE box,%
This leads to the following result:
Apparently comma's are neglected when searching the database. You can not use them in a search query. This leaves you empty handed.
A WORKAROUND
Instead of using a Tag type TV you could use a plain Text type TV and you can use semicolons instead of comma's. Now you can search for:
LIKE box||LIKE %;box;%||LIKE %;box||LIKE box;%
You'll get this result:
- Page 1 - box
- Page 2 - box,container
It works! The only drawback is that a Text TV will not remove the extra spaces surrounding the semicolons, like a Tag TV does. Users inserting spaces after the semicolons will break your query, so you need to slightly adjust it:
LIKE box||LIKE %;box;%||LIKE %; box;%||LIKE %;box||LIKE %; box||||LIKE box;%
It isn't pretty, but it works. In case you're not looking for exact matches, but for the occurrence of the whole word, it would look like this:
LIKE box||LIKE % box %||LIKE % box;%||LIKE %;box %||LIKE %;box;%||LIKE %;box||LIKE % box||LIKE box;%||LIKE box %
Even less than pretty and probably pulling quite a load on your webserver (especially with large sites), but it still works.
ANOTHER WORKAROUND
As an alternative you could put quote marks around each tag. This would make everything a lot easier, but it does require all users to be precise and consistent. Instead of using a Text TV you could create a simple MIGX TV, since MIGX stores everything as a JSON string (automatically quoting all tags). This would require users to enter each tag separately, which makes it quite cumbersome as MIGX opens a modal for each entry. Either way it does reduce the size of queries considerably. For an exact match:
And for the whole word:
LIKE %"box"%||LIKE % box"%||LIKE %"box "||LIKE % box %
A PERMANENT SOLUTION
The second workaround points in the direction of a possible and simple solution to the problem: a TV type that adds quote marks around each tag. I think it would be best if it were to happen automatically and transparently. Meaning that the input type would take care of adding quotes to the comma delimited tags before storing the TV in the database. And the output type would remove the quotes before parsing them to HTML. This way the TV field could look similar to a Tag TV field and the data passed to snippets would also be the same. Only the queries would need to be adjusted.
The TV type could be added to the core (preferably) or someone might make a plug-in. I've tried to do it myself, but my PHP skills are unfortunately not good enough. (a refactor request has been submitted:
#9493)