Another question for you, Dave.
I have a few feeds that have the following in the category column:
"Cameras","Memory Cards","Flash Memory Cards"
I would think that subcategories would cover this but I know your take on subcats and I have to say, I agree. Most feeds are thorough enough or consistent enough to subcats. What I was thinking though, was some sort of tagging system. No heirarchy or category, subcategory dependancies. Just a bunch of tags associated with each product.
Is it possible to make another table with the product id number and up to 5 tags and have them associated with the product table?
Ok, I've set up the extra field but a few things are happening here.
First of all, all of the "tags" are in the same column of the datafeed and they are quoted text, comma separated. When importing them into the tags field, the commas and quotes are stripped leaving the above looking like CamerasMemory CardsFlash Memory Cards in the tags field.
Secondly, what I'm trying to do is keep these values seperate of each other. Ultimately, if you clicked on Flash Memory Cards, it would return all products tagged with Flash Memory Cards.
I was thinking of using another table because the amount of product tags varies from 1-5 but if it can be done within the products table then obviously, that would be better.
Thanks for your help.
Hi Mike,
It sounds like a tag: search modifier, with an index similar to the existing brand and category indexes is what's needed. It's also possible to add code (possibly through a filter) to remove the commas and quotes correctly.
Bear with me and I'll work through the code mods and new files required tomorrow...
Cheers,
David.
Hi Mike,
I'm not sure why the commas and quotes are being stripped in the tags field, unless you added a "normalise" function call within the record handler? If you did, it's worth removing this because reading what you want to do again (keeping the tags separate) it will be best to store them comma separated within the database. Therefore, what I would recommend within the admin__importRecordHandler() function in includes/admin.php, add the following code around about line 238 - just before the /* create dupe_hash value */ comment:
$tags = $record[$admin_importFeed["field_tags"]];
$tags = str_replace("\"","",$tags);
This will remove the " characters, leaving a comma separted string of tags. Finally, in the SQL statement you then need to use the variable $tags, so you should have:
database_safe($tags)
If you're in doubt about the changes to this file feed free to email it over to me and i'll check it over for you.
Next, a new search modifier to look for products containing the requested tag would be required. To do this, look in search.php for the following code (line 53 in the distribution):
case "bw":
Now add the following immediately before this line (we're adding a new CASE to this switch statement):
case "tag":
$sql = "SELECT * , MIN( price ) AS minPrice, MAX( price ) AS maxPrice, COUNT( id ) AS numMerchants FROM `".$config_databaseTablePrefix."products` WHERE tags LIKE '".database_safe($parts[1])."%' GROUP BY name";
$sqlResultCount = "SELECT COUNT(DISTINCT(name)) as resultcount FROM `".$config_databaseTablePrefix."products` WHERE tags LIKE '".database_safe($parts[1])."%'";
$orderBySelection = $orderByDefault;
break;
By doing this, your search script will now support a query such as:
http://www.yoursite.com/search.php?q=tags:Memory+Cards
(remember that a + character is interpreted as a SPACE within a URL)
Finally, a new script similar to brands.php and categories.php is required to display a list of tags and make them a clickable link to the search script using your new tag: modifier. This is slightly more complicated than the brand or category index scripts because we have to read all tags and split them up by the comma, and then display an index of each unique tag. Something like this should do the trick:
tags.php
<?php
require("includes/common.php");
$atoz["items"] = array();
$sql = "SELECT tags FROM `".$config_databaseTablePrefix."products` WHERE tags <> ''";
// create an array to hold each unqiue tag
$alltags = array();
if (database_querySelect($sql,$rows))
{
foreach($rows as $product)
{
$tags = $product["tags"];
$tags = explode(",",$tags);
foreach($tags as $tag)
{
$tag = ucwords(strtolower($tag));
$alltags[$tag] = 1;
}
}
}
// sort the all tags array to get alphabetical list of unique tags
ksort($alltags);
foreach($alltags as $tag => $null)
{
$item = array();
$item["name"] = $tag;
$item["href"] = "search.php?q=tag:".urlencode($tag).":";
$atoz["items"][] = $item;
}
$banner["h2"] = "<strong>Tags A-Z</strong>";
require("html/header.php");
require("html/menu.php");
require("html/searchform.php");
require("html/banner.php");
require("html/atoz.php");
require("html/footer.php");
?>
To link to the tag index from the homepage, simply add the following code to index.php:
print "<a href='tags.php'>Tags</a>";
This should get close to what you want. As you've already setup the database with the tags field the only tricky bit may be the mods to the import record handler as i'm not sure why the quotes and commas would have been stripped - but once you get the tags field importing as comma separated values it should work fine from there!
Hope this helps!
Cheers,
David.
David, thanks a bunch!
I removed the normalise function that I had previously added (in an attempt to do this myself) and added your code instead. I had already had the database_safe($tags) in the sql. The tags.php file is awesome and, as far as I can tell, works just fine. I did remove the uppercase words and lowercase string statement but that was because a few of the tags had something like "This and That" and I didn't need the word "and" made to "And".
The problem I seem to be having is with the new case being added to the switch statement in search.php.
Keeping with the above, my tags field within the database now looks like this:
Cameras,Memory Cards,Flash Memory Cards
Which is what I believe it should look like. The problem I'm having is that the only tags that return a result are the first level tags. In other words, I get results for Cameras but not Memory Cards or Flash Memory Cards. I imported the entire datafeed and it remained consistent throughout 8,000 products. The first tag in the field always returned the correct results and all of the other tags in the field failed to return any result.
Any ideas?
Ooops, my mistake! I based the code for the tag: handler on the bw: (begins with) handler, so it only has the wildcard character (%) after the search text - which is why it only works for the first tag in the list... The correct code is as follows:
case "tag":
$sql = "SELECT * , MIN( price ) AS minPrice, MAX( price ) AS maxPrice, COUNT( id ) AS numMerchants FROM `".$config_databaseTablePrefix."products` WHERE tags LIKE '%".database_safe($parts[1])."%' GROUP BY name";
$sqlResultCount = "SELECT COUNT(DISTINCT(name)) as resultcount FROM `".$config_databaseTablePrefix."products` WHERE tags LIKE '%".database_safe($parts[1])."%'";
$orderBySelection = $orderByDefault;
break;
That should do the trick - sorry about that...
Cheers,
David.
Great Stuff Dave! Works like a charm!
I've also made this code that will put the tags on the product pages (almost like breadcrumbs) and wanted to share.
<?php if ($mainProduct["tags"]): ?>
<div id="tags">
<?php
$tagsarray = explode(",",$mainProduct["tags"]);
$endarray = end($tagsarray);
foreach ($tagsarray as $tag)
{
if ($tag == $endarray) {
print "<a href='/search.php?q=tag:$tag'>$tag</a>";
}
else {
print "<a href='/search.php?q=tag:$tag'>$tag</a> <small>>></small> ";
}
}
?>
</div>
<?php endif; ?>
I wonder if there is a way to force the script to use a certain merchant's "tags". Also, is there a way to make the script import more than one field from the datafeed into the tags field of the database.
oops, one other thing, Dave.
When using the same field for the optional category and tags, it strips the commas and I can't seem to figure out a way around it.
Hi Dave,
You could use the "Text After" filter to add additional fields to the tags field - including the necessary commas within your text after string as required. To do this, you would need to modify the following files:
admin/feeds_filters.php
admin/feeds_filters_configure.php
Instructions for doing this are in the following thread:
http://www.pricetapestry.com/node/865
You will then be able to register filters against your new tags field.
With regards to the commas being removed if you register the same field as category and tags, this is because the category field is normalised, and as this function works on the data in the record, the same normalised value is then what is used for the tags field. This happens because of the following code on line 170 of includes/admin.php:
$record[$admin_importFeed["field_category"]] = tapestry_normalise($record[$admin_importFeed["field_category"]]);
Option 1 is to allow the comma by adding it in the optional "allow" fields to the tapestry_normalise() function, like this:
$record[$admin_importFeed["field_category"]] = tapestry_normalise($record[$admin_importFeed["field_category"]],",");
The second option is to copy the category into a temporary variable and then normalise that, which will leave the field untouched for later use in the tags field. To use this option, change the above line as follows:
$temp_category = tapestry_normalise($record[$admin_importFeed["field_category"]]);
...and then lower down the function look for the following code (starting at line 206):
/* construct category value if required */
if ($admin_importFeed["field_category"])
{
$category = $record[$admin_importFeed["field_category"]];
}
...and change this as follows:
/* construct category value if required */
if ($admin_importFeed["field_category"])
{
$category = $temp_category;
}
Hope this helps!
Cheers,
David.
Thanks again Dave, the category problem is solved. I chose the second option because it kept the original structure of my categories in tact.
Regarding the text after filter, I had already sort of done this but couldn't (and still can't) get it to register a different field. I'm not entirely sure that this is possible but maybe I'm just misunderstanding something here.
I don't want to add any text after the tags field. What I was wondering was if it's possible to import two field from the datafeed into the tags field? In other words, looking at it from the datafeed, import FIELD2 and FIELD4 into the "tags" field. I'm not sure if this possible but figured you would know.
Hi Mike,
You should be able to do this with the field value replacement placeholders that are supported by the Text After filter. To use your example, you could register FIELD2 as the tags field, and then your Text After filter would contain:
%FIELD4%
This will bring in the value of FIELD4 in the current record. Note the leading SPACE - this is important if you want to separate the values. Following on from the earlier mod, you might instead want to make it a , for example:
,%FIELD4%
Hope this helps!
Cheers,
David.
Hi Mike,
It should reasonably straight forward to do something here; most easily by adding a new field to the products table to hold "tags" rather than setting up an associated table. First, you would need to add the new field to the database and add the necessary admin code for you to register the field against feeds. Instructions for doing this are in the following thread - and I suggest you use a field name "tags":
http://www.pricetapestry.com/node/313
There are several options for incorporating the tags field into the search - some more complicated than others and would require new indexes on the database. One option is a simple tag: modifier (like category: brand: etc.) that would search the tag field. Alternatively, the default search case could be modified to search product name AND tags field. Let me know what you had in mind for search and help out with the code mods required for that...
Cheers,
David.