You are here:  » Category based on product title


Category based on product title

Submitted by knight01 on Tue, 2008-02-05 02:44 in

Is there a way to map a category based on the name of a product? A datafeed I'm working with has products in one category that I want to split into three different categories based on the words in the title.

i.e.

Category: hdtv tv
product: 42" sony plasma hdtv
product: 42" plasma tv by hitachi
product: 60" toshiba DLP TV
product: 32" sony hdtv lcd

What I'm trying/wanting to do is map these products to a category based on the words in the title lcd, plasma or dlp.

Any tips and ideas?

Submitted by support on Tue, 2008-02-05 14:06

Hi,

This can be done with a reasonably straight forward mod to includes/admin.php, to set the category based on a string comparison of the product name.

Look for the following code within the admin__importRecordHandler() function, beginning at around line 208.

    /* construct category value if required */
    if ($admin_importFeed["field_category"])
    {
      $category = $record[$admin_importFeed["field_category"]];
    }
    elseif($admin_importFeed["user_category"])
    {
      $category = $admin_importFeed["user_category"];
    }
    else
    {
      $category = "";
    }

Assuming that you want to set a "dynamic category" (for want of a better name), the this can be done by modifying the code above as follows:

    /* construct category value if required */
    if ($admin_importFeed["field_category"])
    {
      $category = $record[$admin_importFeed["field_category"]];
    }
    elseif($admin_importFeed["user_category"])
    {
      $category = $admin_importFeed["user_category"];
    }
    else
    {
      // strtolower the product name so that comparison is case insensitive
      $pn = strtolower($record[$admin_importFeed["field_name"]]);
      // create an array of keywords => categories
      $kw = array();
      $kw["lcd"] = "HDTV TV";
      $kw["plasma"] = "HDTV TV";
      $kw["dlp"] = "HDTV TV";
      foreach($kw as $k => $v)
      {
        if (strpos($pn,$k)!==FALSE) { $category = $v; break; }
      }
    }

You can then extend the $kw array as much as you like, adding more keyword > category combinations. For performance reasons the loop will exit at the first match.

Hope this helps!
Cheers,
David.

Submitted by Mik3 on Sat, 2008-04-12 11:59

David,

I've used this code successfully before and it's been fantastic and saved loads of time... what I was now wondering is how could I modify it to pick words out of the product description for the category and brand fields? I'm particularly looking at putting citiess and counties to be specific in either brand or category.

Submitted by support on Sun, 2008-04-13 11:30

Hi,

What you could do is concatenate other fields to the $pn variable in the second part of this modification; for example to add the description field (so it would search product name AND description for the list of keywords) try:

      $pn = "";
      $pn .= strtolower($record[$admin_importFeed["field_name"]]);
      $pn .= strtolower($record[$admin_importFeed["field_description"]]);
      // create an array of keywords => categories
      $kw = array();
      $kw["lcd"] = "HDTV TV";
      $kw["plasma"] = "HDTV TV";
      $kw["dlp"] = "HDTV TV";
      foreach($kw as $k => $v)
      {
        if (strpos($pn,$k)!==FALSE) { $category = $v; break; }
      }

Cheers,
David.

Submitted by richard on Sun, 2010-01-17 10:06

Hi David

With this feature the routine breaks after the dynamic category has been created. I would like to have two or three categories created for each product.

How would I change the above code so that it does not stop when it gets it first match?

I understand that this will degrade server performance but my database is small (circa 1000 products).

Many thanks

Regards

Richard

Submitted by support on Sun, 2010-01-17 21:52

Hi Richard,

Bear in mind that a single product can only be mapped to a single category; however what may be desirable in your situation is to order the category array by preference (least > most preferred); and then removing the break - so that the most applicable category is the one that is applied - in which case simply removing the break; statement leaving you with:

      foreach($kw as $k => $v)
      {
        if (strpos($pn,$k)!==FALSE) { $category = $v; }
      }

...should do the trick;

Cheers,
David.

Submitted by richard on Sun, 2010-01-17 23:33

Hi David

Thanks for your idea.

Given the constraint of one category per product, what would I need to do with this code to introduce a secondary category e.g. category1 for 1st attribute and category2 for a 2nd attribute?

I would not need the categories.php page but just urls that offer ability to access the products by category2

Regards

Richard

Submitted by HJW on Mon, 2010-01-18 01:35

Hi David,

This looks like a very useful mod, could you tell me how to implement this on the new PT distribution?

Regards

Hayden

Submitted by support on Mon, 2010-01-18 09:24

Hello Hayden,

Sure, look for the following code beginning at line 164 (11/09A) or line 187 (12/10A/B) in includes/admin.php:

    if ($importRecord["category"])
    {
      $importRecord["category"] = tapestry_normalise($importRecord["category"]);
    }

...and then add the dynamic category code as follows immediately AFTER this point:

      $pn = "";
      $pn .= strtolower($importRecord["name"]);
      $pn .= strtolower($importRecord["description"]);
      // create an array of keywords => categories
      $kw = array();
      $kw["lcd"] = "HDTV TV";
      $kw["plasma"] = "HDTV TV";
      $kw["dlp"] = "HDTV TV";
      foreach($kw as $k => $v)
      {
        if (strpos($pn,$k)!==FALSE) { $importRecord["category"] = $v; break; }
      }

Cheers,
David.

Submitted by HJW on Tue, 2010-01-19 15:25

Hi David,

Excellent, many thanks once again.

Regards

Hayden

Submitted by bat on Sun, 2011-11-20 12:27

This sounds perfect, though I've just tried it out and it doesn't seem to be working for me. I'm using the latest version. Shall I send you my includes/admin.php ?
Also, if the products that contain the keyword are in a category that's already been mapped to a different category, would this matter? I've tried both mapped and unmapped categories, but the items remain in the same category.
Additionally, if I wanted to include two keywords that both HAD to be in the product name to be mapped to a category, how would I type that? Just put a space between or a + sign?
Many thanks David!

Submitted by support on Sun, 2011-11-20 13:14

Hi bat,

For the latest version, make sure to use the instructions in this comment which i've updated to show lines numbers for all latest versions.

This should not be affected by any additional category mappings, unless the manually set category happened to a appear as an alternative for another category but that is the only condition under which it would be changed.

To use multiple keywords with AND logic, have a go with the following as the replacement:

      $pn = "";
      $pn .= strtolower($importRecord["name"]);
      $pn .= strtolower($importRecord["description"]);
      // create an array of keywords => categories
      $kw = array();
      $kw["lcd,tv"] = "HDTV TV";
      $kw["plasma"] = "HDTV TV";
      $kw["dlp"] = "HDTV TV";
      foreach($kw as $k => $v)
      {
        $found = 0;
        $words = explode(",",$k);
        foreach($words as $word)
        {
          if (stripos($pn,$word)!==FALSE) $found++;
        }
        if ($found == count($words))
        {
          $importRecord["category"] = $v;
          break;
        }
      }

(i've also modified the above to use stripos to make the comparison case insensitive)

With this version you can provide the keywords in a comma separated list, e.g.

      $kw["lcd,tv"] = "HDTV TV";

..would require the match string to contain "lcd" AND "tv"...

Cheers,
David.
--
PriceTapestry.com

Submitted by bat on Sun, 2011-11-20 13:21

Bravissimo! David, as always, you are a legend! I wish i had your brain. Thank you!

Submitted by bat on Tue, 2012-11-13 17:52

Just wondering, could the above code be added into includes/admin.php and modified to do the same for Brands?

Submitted by support on Tue, 2012-11-13 19:24

Hi bat,

Sure - in addition to the above mod; have a go with:

      // create an array of keywords => brands
      $kw = array();
      $kw["alternative1,alternative2,etc."] = "This Brand";
      $kw["alternative3,alternative4,etc."] = "That Brand";
      // etc.
      foreach($kw as $k => $v)
      {
        $found = 0;
        $words = explode(",",$k);
        foreach($words as $word)
        {
          if (stripos($pn,$word)!==FALSE) $found++;
        }
        if ($found == count($words))
        {
          $importRecord["brand"] = $v;
          break;
        }
      }

Note that the above is in addition to the previous modification, so does not duplicate the code to create the $pn variable, which is a concatenation of the product name and description...

Cheers,
David.
--
PriceTapestry.com

Submitted by bat on Tue, 2012-11-13 19:48

Thanks David. This is better for me, as I use it for the categories too.
I popped the above code under the similar code as for categories, but in the brands section, but I put this above what you've wrote. Should I remove that then?

  $pn = "";
  $pn .= strtolower($importRecord["name"]);

Submitted by support on Tue, 2012-11-13 19:54

Ah - it's fine to keep it there - if the category version of the code had preceeded it there's actually no need to replicate the code as the $pn variable will already have been created - but for fine control and in particular if you only want the brand testing to apply to the name only then it would be required...

Cheers,
David.
--
PriceTapestry.com

Submitted by bat on Thu, 2012-11-15 15:12

Works a treat, thanks David!

Submitted by BobL on Tue, 2013-09-17 17:49

Bob L.

Hi David hope all is well.
Have 2 questions.
Will this mode work for the latest version 13/03A ?
And will spaces and brackets () also work between the quotes?

Submitted by support on Thu, 2013-09-19 10:11

Hello Bob,

To use this method with 13/03A, look for the following code beginning at line 187 in includes/admin.php:

      $importRecord["category"] = "";

...and REPLACE with:

      $importRecord["category"] = "";
      // strtolower the product name so that comparison is case insensitive
      $pn = strtolower($record[$admin_importFeed["field_name"]]);
      // create an array of keywords => categories
      $kw = array();
      $kw["lcd"] = "HDTV TV";
      $kw["plasma"] = "HDTV TV";
      $kw["dlp"] = "HDTV TV";
      foreach($kw as $k => $v)
      {
        if (strpos($pn,$k)!==FALSE) { $importRecord["category"] = $v; break; }
      }

Spaces and brackets can be used between the quotes of the keywords to match.

However, a more elegant solution documented more recently is in this comment which modified Category Mapping to consider the product name field as well as the category field, so with that you could replicate this using either =Exact Match or Keyword match alternatives - just as with Category Mapping...

Cheers,
David.
--
PriceTapestry.com

Submitted by stevebi on Wed, 2014-12-10 21:37

Hi David,

This is what I was looking for my big database.

Can I use this code at 14/0A version?

And may I ask if I should use a cron in order to get it work

Regards

Steve

Submitted by support on Thu, 2014-12-11 09:27

Hello Steve,

Above modification is absolutely fine for 14/06A. No need to set-up separate CRON as the modification is to the import record handler so it will be automatically applied at import time (whatever method of import is used).

Cheers,
David.
--
PriceTapestry.com

Submitted by stevebi on Thu, 2014-12-11 09:58

Hello David,

And thank you for your fast reply.

May I ask if this is applied to subcategories also?
Will I be able to map manually categories after the modification?

Regards

S

Submitted by stevebi on Thu, 2014-12-11 10:02

Hi David,

Will it work for Greek characters?

Regards

S

Submitted by support on Thu, 2014-12-11 10:22

Hello Steve,

Shouldn't be any problem with UTF-8 Greek characters. The above mod is applied to the category field _before_ the Category Hierarchy code that you are using is applied, so the category set by the above can then be mapped (or reverse mapped) into your hierarchy at the required point.

Cheers,
David.
--
PriceTapestry.com

Submitted by bat on Mon, 2016-10-17 17:07

Hi David,
Can the category mapping based on product name/keywords, as described in this thread, still be used in 15/09 or is there an alternative way?

Submitted by support on Mon, 2016-10-17 17:25

Hi,

To modify Category Mapping to consider product name as well as category, edit includes/admin.php and look for the following code beginning at line 383:

    if (isset($admin_importCategoryMappings["=".$importRecord["category"]]))
    {
      $importRecord["category"] = $admin_importCategoryMappings["=".$importRecord["category"]];
    }

...and REPLACE with:

    if (isset($admin_importCategoryMappings["=".$importRecord["name"]]))
    {
      $importRecord["category"] = $admin_importCategoryMappings["=".$importRecord["name"]];
    }
    elseif (isset($admin_importCategoryMappings["=".$importRecord["category"]]))
    {
      $importRecord["category"] = $admin_importCategoryMappings["=".$importRecord["category"]];
    }

And then look for the following code at line 401:

  if (strpos($importRecord["category"],$word) !== FALSE) $found++;

...and REPLACE with:

  if (strpos($importRecord["category"].$importRecord["name"],$word) !== FALSE) $found++;

Cheers,
David.
--
PriceTapestry.com

Submitted by bat on Fri, 2017-12-29 14:54

Hi David,
I can't seem to get this to work. I've put the code in but they aren't being mapped to the category. My installation is using category hierarchy

Thanks

Submitted by support on Fri, 2017-12-29 15:20

Hi,

The above would apply to basic category mapping - to do the same for Category Hierarchy Mapping, edit includes/admin.php and look for the following code beginning at line 416:

    if (isset($admin_importCategoryHierarchyMappings["=".$importRecord["category"]]))
    {
      $importRecord["categoryid"] = $admin_importCategoryHierarchyMappings["=".$importRecord["category"]];
    }

...and REPLACE with:

    if (isset($admin_importCategoryHierarchyMappings["=".$importRecord["name"]]))
    {
      $importRecord["categoryid"] = $admin_importCategoryHierarchyMappings["=".$importRecord["name"]];
    }
    elseif (isset($admin_importCategoryHierarchyMappings["=".$importRecord["category"]]))
    {
      $importRecord["categoryid"] = $admin_importCategoryHierarchyMappings["=".$importRecord["category"]];
    }

And then the following code at line 434:

  if (strpos($importRecord["category"],$word) !== FALSE) $found++;

...and REPLACE with:

  if (strpos($importRecord["category"].$importRecord["name"],$word) !== FALSE) $found++;

Cheers,
David.
--
PriceTapestry.com

Submitted by bat on Fri, 2017-12-29 15:36

Hi David,
Thanks for this however it doesn't seem to work on mine.

Submitted by support on Sat, 2017-12-30 11:57

Hi,

Please could you email me the modified includes/admin.php and I'll check it out further with you...

Thanks,
David.
--
PriceTapestry.com