<HTML>
<HEAD>
<TITLE>Categories</TITLE>
<LINK REV="made" HREF="mailto:">
</HEAD>
<BODY>
<A NAME="__index__"></A>
<!-- INDEX BEGIN -->
<UL>
<LI><A HREF="#name">NAME</A></LI>
<LI><A HREF="#version">VERSION</A></LI>
<LI><A HREF="#description">DESCRIPTION</A></LI>
<LI><A HREF="#synopsis">SYNOPSIS</A></LI>
<LI><A HREF="#syntax">SYNTAX</A></LI>
<LI><A HREF="#author">AUTHOR</A></LI>
</UL>
<!-- INDEX END -->
<HR>
<P>
<H1><A NAME="name">NAME</A></H1>
<P>Categories - Create and process categories within MySQL DB</P>
<P>
<HR>
<H1><A NAME="version">VERSION</A></H1>
<P>Categories.pm ver.1.0</P>
<P>
<HR>
<H1><A NAME="description">DESCRIPTION</A></H1>
<P>Categories allows you to create and process categories (for products/directories/shops and etc...)</P>
<P>
<HR>
<H1><A NAME="synopsis">SYNOPSIS</A></H1>
<PRE>
There is an example that you may use in your own CGI scripts:</PRE>
<PRE>
# --- Script begin here ---
use Categories;</PRE>
<PRE>
# 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!
</PRE>
<PRE>
$obj = Categories->new(database => 'catsdb', user => 'db_user', pass => 'db_pass', host => 'localhost');
# OR
# $obj = Categories->new(dbh => $mysql_dbh_handler);</PRE>
<PRE>
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);
my $one_id = $obj->add(type=>'item',name=>'The One',category=>$film_id);
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);
my $xp20_id = $obj->add(type=>'item',name=>'Athlon XP 2000+',category=>$cpu_id);
my $xp21_id = $obj->add(type=>'item',name=>'Athlon XP 2100+',category=>$cpu_id);
my $hdd1_id = $obj->add(type=>'item',name=>'Maxtor 80 GB',category=>$hdd_id);
my $hdd2_id = $obj->add(type=>'item',name=>'Maxtor 120 GB',category=>$hdd_id);</PRE>
<PRE>
# 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!</PRE>
<PRE>
my @res = $obj->find('search'=>'The Matrix','sort'=>'ID','by'=>'NAME','filter'=>'ALL','multiple'=>YES,
'route'=>YES,'preload'=>YES,'partial'=>NO,'reverse'=>NO,'sort'=>'NAME');
if(scalar(@res))
{
foreach $l (@res)
{
my ($type,$id,$parent_category,$name,$route_path) = @$l;
print "Type: $type<BR>\n";
print "ID: $id<BR>\n";
print "PARENT: $parent_category<BR>\n";
print "NAME: $name<BR>\n";
$route_path =~ s~//~\\~sgi;
$route_path =~ s~\\(.*?)\x0~\\~sgi;
print "PATH: $route_path<BR>\n";
}
}
print "<HR>";</PRE>
<PRE>
# Modify: Change PARENT/CID and/or NAME
$obj->modify(id=>$xp21_id,type=>'item',name=>'Duron 1300 MHz');
$obj->modify(id=>$comp_id,type=>'category',name=>'PC');
$obj->modify(id=>$cpu_id,type=>'category',newcid=>0);</PRE>
<PRE>
$obj->deep_traverse('preload'=>Y,'id'=>0,'level'=>0,'path'=>'//','eval'=>\&Walk,'sort'=>'NAME');</PRE>
<PRE>
# Delete ROOT category, so all items/categories are deleted!
$obj->del(type=>'category',id=>0);
}
else
{
print $Categories::error;
}</PRE>
<PRE>
sub Walk
{
my $self = shift;
my %inp = @_;</PRE>
<PRE>
my $id = $inp{'id'};
my $level = $inp{'level'};
my $separator = $inp{'separator'};
my $path = $inp{'path'};
my $name = $inp{'name'};
my $type = $inp{'type'};</PRE>
<PRE>
$path =~ s~$separator~\\~sgi;
$path =~ s~\\(.*?)\x0~\\~sgi;
print $path."$name<BR>";
}
# --- Script ends here ---</PRE>
<P>
<HR>
<H1><A NAME="syntax">SYNTAX</A></H1>
<PRE>
That is simple function reference:
</PRE>
<PRE>
$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!</PRE>
<PRE>
$state = $object->is_tables_exists(name=>'name_of_category_object');
Check database structure only.</PRE>
<PRE>
$state = $object->create_tables(name=>'name_of_category_object');
Create table structure (database should exist!)</PRE>
<PRE>
$state = $object->clear_cache();
Clear categories cache.</PRE>
<PRE>
@cats = $object->preload_categories(name=>'name_of_category_object', sort=>'NAME', reverse=>'N');
Create categories cache and return array of all categories. @cats is array of references to hashes;
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'};</PRE>
<PRE>
@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.
@res is array of reference to arrays. Every element of array (dereferenced is array too) has follow structure:
[0] - 'I' or 'C' (Item or Category),
[1] - ID of Item/Category,
[2] - PARENT (category ID),
[3] - NAME of Item/Category,
[4] - If route=>'Y' this will be full path to this Item/Category (//id\x0Computers//id\x0CPU...)
where respective ID is separated from respective NAME with \x0 !</PRE>
<PRE>
$id = $object->add(type=>'ITEM', category=>'0', name=>'Name_Of_Element', check=>'N');
Where:
type - is type of element ('ITEM' and 'CATEGORY'),
category - is ID of parent (0 is root),
name - name of new item/category,
check - test tables structure.
$id is ID of created element.</PRE>
<PRE>
$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.</PRE>
<PRE>
$cnt = $object->modify(type=>'ITEM', id=>'id_of_element', newcid=>'id_of_new_parent', check=>'N',
name=>'new_name_of_element', preload=>'Y');
Where:
type - is type of element ('ITEM' and 'CATEGORY'),
id - is ID of Item/Category,
check - test tables structure,
name - new name item/category (if you dismiss filed, NAME 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.
</PRE>
<PRE>
$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);</PRE>
<PRE>
$cnt = $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:
</PRE>
<PRE>
&$evala($self,'id'=>$id,'level'=>$level,'type'=>$whereis,'path'=>$path,
'name'=>$item_name,'separator'=>$separator);
where 'name' will be available only for Items (type=>'I'), but not for categories (type=>'C')</PRE>
<PRE>
$cnt = $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)!</PRE>
<P>
<HR>
<H1><A NAME="author">AUTHOR</A></H1>
<PRE>
Julian Lishev - Bulgaria, Sofia,
e-mail: julian@proscriptum.com,
www.proscriptum.com</PRE>
</BODY>
</HTML>