NAME

Categories - Create and process categories within MySQL DB

VERSION

Categories.pm ver.1.0

DESCRIPTION

    Categories allows you to create and process categories (for products/directories/shops and etc...)

SYNOPSIS

There is an example that you may use in your own CGI scripts:

# --- Script begin here ---
use Categories;

# NOTE: new() method will create needed DB structure in MySQL (database & tables) if they not exist!
#       Please create database before execute this script or DB USER must have privilege to create DBs!

$obj = Categories->new(database => 'catsdb', user => 'db_user', pass => 'db_pass', host => 'localhost');
     # OR
     # $obj = Categories->new(dbh => $mysql_dbh_handler);

if($obj)
 {
  my $comp_id = $obj->add(type=>'category',name=>'Computers',category=>0);
  my $film_id = $obj->add(type=>'category',name=>'Films',category=>0);
  my $matr_id = $obj->add(type=>'item',name=>'The Matrix',category=>$film_id,value=>'');
  my $one_id  = $obj->add(type=>'item',name=>'The One',category=>$film_id,value=>'');
  my $cpu_id  = $obj->add(type=>'category',name=>'CPU',category=>$comp_id);
  my $hdd_id  = $obj->add(type=>'category',name=>'HDD',category=>$comp_id);
  my $xp18_id = $obj->add(type=>'item',name=>'Athlon XP 1800+',category=>$cpu_id,value=>'');
  my $xp20_id = $obj->add(type=>'item',name=>'Athlon XP 2000+',category=>$cpu_id,value=>'');
  my $xp21_id = $obj->add(type=>'item',name=>'Athlon XP 2100+',category=>$cpu_id,value=>'');
  my $hdd1_id = $obj->add(type=>'item',name=>'Maxtor 80 GB',category=>$hdd_id,value=>'30 months warranty');
  my $hdd2_id = $obj->add(type=>'item',name=>'Maxtor 120 GB',category=>$hdd_id,value=>'36 months warranty');
  
  my @res = $obj->read(path=>'//Computers//HDD//*',sort=>'ID',preload=>YES,reverse=>NO,partial=>'YES');
  print "<HR>";
  if(scalar(@res))
    {
     foreach $l (@res)
      {
        my ($id,$parent_category,$name,$value,$route_path) = @$l;
        print "ID:     $id<BR>\n";
        print "PARENT: $parent_category<BR>\n";
        print "NAME:   $name<BR>\n";
        print "VALUE:  $value<BR>\n";
        $route_path =~ s~//~\\~sgi;
        $route_path =~ s~\\(.*?)\x0~\\~sgi;
        print "PATH:   $route_path<BR>\n";
        print "<HR>";
      }
    }
  
  # Find categories and items (filter=>ALL) that has NAME (by=>NAME) 'The Matrix' order by ID (sort=>ID)
  # and return multiple results (multiple=>'YES') if available, also return rout path to this element 
  # (route=>'YES') using category cache (preload=>'YES') to speed up searching. However 'preload' option may be
  # worse if categories table is too long, because script load whole table and may crush if not enough memmory!
  
  my @res = $obj->find('search'=>'The Matrix','sort'=>'ID','by'=>'NAME','filter'=>'ALL','multiple'=>'YES',
                       'route'=>'YES','preload'=>'YES','partial'=>'NO','reverse'=>'NO');
  if(scalar(@res))
    {
     foreach $l (@res)
      {
        my ($type,$id,$parent_category,$name,$value,$route_path) = @$l;
        print "Type:   $type<BR>\n";
        print "ID:     $id<BR>\n";
        print "PARENT: $parent_category<BR>\n";
        print "NAME:   $name<BR>\n";
        print "VALUE:  $value<BR>\n";
        $route_path =~ s~//~\\~sgi;
        $route_path =~ s~\\(.*?)\x0~\\~sgi;
        print "PATH:   $route_path<BR>\n";
      }
    }
   print "<HR>";
   
   # Modify: Change PARENT/CID and/or NAME
   $obj->modify(id=>$xp21_id,type=>'item',name=>'Duron 1300 MHz',value=>'6 months warranty');
   $obj->modify(id=>$comp_id,type=>'category',name=>'PC');
   $obj->modify(id=>$cpu_id,type=>'category',newcid=>0);
   
   $obj->deep_traverse('preload'=>'YES','id'=>0,'level'=>0,'path'=>'//','eval'=>\&Walk,'sort'=>'NAME');
   
   # Delete ROOT category, so all items/categories are deleted!
   $obj->del(type=>'category',id=>0);
  }
 else
  {
   print $Categories::error;
  }
  
sub Walk
{
 my $self = shift;
 my %inp  = @_;

 my $id              = $inp{'id'};
 my $level           = $inp{'level'};
 my $separator       = $inp{'separator'};
 my $path            = $inp{'path'};
 my $name            = $inp{'name'};   # ITEM only ($type='I')!
 my $value           = $inp{'value'};  # ITEM only ($type='I')!
 my $type            = $inp{'type'};

 $path =~ s~$separator~\\~sgi;
 $path =~ s~\\(.*?)\x0~\\~sgi;
 print $path."$name"."[$value]<BR>";
}
# --- Script ends here ---

SYNTAX

That is simple function reference:

# Create object of Categories type
$object = Categories->new(database=>'catsdb', user=>'db_user', pass=>'db_pass', host=>'localhost', 
                          port=>'3306', create=>'Y', checkdb=>'Y', name=>'catdb', dbh=>$connect_db_handler);
   Where:
   database  - is your DB where categories (tables) will be placed. If database not exist module
               will try to create one for you, using supplied user and password. [REQUIRED if $dbh empty]
   user/pass - is your DB user and password [REQUIRED if $dbh empty]
   host      - MySQL DB host
   port      - your MySQL port
   create    - module will attempt to create DB and/or tables
   checkdb   - module will try to check DB structure
   name      - name of category object
   dbh       - you can supply already connected database handler instead of database/user/pass/host/port!
   


# Test database structure (tables only)
$state = $object->is_tables_exists(name=>'name_of_category_object');
   
   

# Create database structure (tables only - database must exist!)
$state = $object->create_tables(name=>'name_of_category_object');
   Create table structure (database should exist!)



# Clear categories cache
$state = $object->clear_cache();



# Reload/create categories cache and return array of all categories. @cats is array of references to hashes;
@cats = $object->preload_categories(name=>'name_of_category_object', sort=>'NAME', reverse=>'N');
   Where:
          sort      - is name of column (order by),
          reverse   - reverse results (DESC)
   HINT: $ref = $cats[0]; %hash = %$ref; $name = $hash{'NAME'}; $id = $hash{'ID'}; $parent = $hash{'PARENT'};
   


# Find category/item by 'by' column, searching by 'search' keyword (may be on part of word 'partial'=>'Y');
# also it may return full path to located item/category (route=>'Y') using categories cache (preload=>'Y')
@res = $object->find(caseinsensitive=>'Y', filter=>'ITEMS', multiple=>'Y', by=>'ID', sort=>'NAME',
                     reverse=>'N', partial=>'N', search=>'keyword', check=>'N', route=>'N',
                     separator=>'//', preload=>'Y');
   Where:
         caseinsensitive  - search is caseinsensitive,
         filter           - define where sub must search (ITEMS,CATEGORIES,ALL),
         multiple         - allows muliple results,
         by               - search BY column,
         sort             - 'order by' all results,
         reverse          - reverse all results,
         partial          - allows partial search ( LIKE %something%),
         search           - search keyword,
         check            - test tables structure,
         route            - find path to root,
         separator        - use follow separator to separate categories in route path,
         preload          - allows categories cache.
   Method returns array of references to arrays results in follow syntax:
         @res = ($ref_1[,$ref_2,...]), where $ref_n is reference to array with follow structure:
         @$ref_n = ([0],[1],[2],[3],[4],[5]);
         where:
         [0] - 'I' or 'C' (Item or Category),
         [1] - ID of Item/Category,
         [2] - PARENT (category ID),
         [3] - NAME of Item/Category,
         [4] - VALUE of Item (ONLY) or empty string if Category,
         [5] - If route=>'Y' this will has follow syntax:
               $separator.$ID."\x0".$CATEGORY_NAME[$separator...]



# Add ITEM/CATEGORY to categories tree. If proceed ITEM you can set 'value' to it.
$id = $object->add(type=>'ITEM', category=>'0', name=>'Name_Of_Element', check=>'N', value=>'Items only');
    Where:
          type      - is type of element ('ITEM' and 'CATEGORY'),
          category  - is ID of parent (0 is root),
          name      - name of new item/category,
          value     - value of item (only)
          check     - test tables structure.
          $id is ID of created element.



# Delete ITEM/CATEGORY (recursive) by it's own ID
$cnt = $object->del(type=>'ITEM', id=>'0', check=>'N', preload=>'Y');
     Where:
           type      - is type of element ('ITEM' and 'CATEGORY'),
           id        - is ID of Item/Category (0 is root),
           check     - test tables structure,
           preload   - allows categories cache.
           $cnt is number of affected(deleted) rows.



# Modify(update/rename/move) given ITEM/CATEGORY. If some parameter missed method will not change
# current ITEM value (name,parent,value)
$cnt = $object->modify(type=>'ITEM', id=>'id_of_element', newcid=>'id_of_new_parent', check=>'N',
                       name=>'new_name_of_element', value=>'Items only', preload=>'Y');
     Where:
           type      - is type of element ('ITEM' and 'CATEGORY'),
           id        - is ID of Item/Category,
           check     - test tables structure,
           name      - new name of item/category (if you dismiss filed, NAME will not be affected!),
           value     - new value of item (if you dismiss filed, VALUE will not be affected!),
           newcid    - new Parent category ID (if you dismiss filed, PARENT will not be affected!),
           preload   - allows categories cache.
           $cnt is number of affected(deleted) rows.



# Traverse category tree (in width) and calling 'eval' callback sub for any found category!
$cnt = $object->traverse(cid=>'id_of_category', eval=>\&callback_sub', check=>'N',
                         sort=>'NAME', reverse=>'N', preload=>'Y');
     This sub traverse in width.
     Where:
           cid      - ID of category that should be traversed,
           eval     - reference to sub that will be called for every category,
                      it will be called as: &$eval($self,'id'=>$current,'parent'=>$cid);



# Traverse category tree (in deep) and calling 'eval' callback sub for any found CATEGORY/ITEM!
$object->deep_traverse(id=>'id_of_category', level=>'0', separator=>'//', path=>'//',
                       eval=>\&callback_sub', sort=>'NAME', reverse=>'N', check=>'N',
                       preload=>'Y');
     deep_traverse is recursive sub and it traverse in deep. At fist step level should be '0' and
     path '//' (like separator); eval is also reference to callback sub and it will be called as:
     
     &$evala($self,'id'=>$id,'level'=>$level,'type'=>$whereis,'path'=>$path,
             'name'=>$item_name,'value'=>$item_value,'separator'=>$separator);
     where 'name'/'value' will be available only for Items (type=>'I'), but not for categories (type=>'C')
     
     

# Load found categories/items for 'cid' category only! (no recurse)
@res = $object->load_category(cid=>'id_of_category', sort=>'NAME', reverse=>'N', preload=>'Y');
     This method will load only Items/Categories of 'cid' category (without recurse)!



# Read category/item[s] properties(data) searching by registry-like path (only by 'NAME')!
@res = $object->read(path=>'//path//to//category//item', sort=>'ID', reverse=>'N', preload=>'Y',
                     partial=>'N', caseinsensitive=>'Y', separator=>'//', preload=>'Y', check=>'N');
     This method try to locate category or item[s] only by 'NAME' starting by 'root' category(PARENT=0),
     Where:
           path      - is 'registry'-like path to category/item[s]. If path ends with '//' (separator)
                       then searching is proceed for category, other else for item. If name of item is
                       '*' then all items of this 'path' will be fetched!
           partial   - This allows searhing for partial items (not for path or categories)! Moreover to
                       use '*' feature you must set partial=>'YES'
     Method returns array of references to arrays results in follow syntax:
           @res = ($ref_1[,$ref_2,...]), where $ref_n is reference to array with follow structure:
           @$ref_n = ($ID,$PARENT_ID,$NAME,$VALUE,$PATH);
           $PATH has follow syntax:  $separator.$ID."\x0".$CATEGORY_NAME[$separator...]

AUTHOR

Julian Lishev - Bulgaria, Sofia, 
e-mail: julian@proscriptum.com, 
www.proscriptum.com