Classification.php
Go to the documentation of this file.
00001 <?PHP 00002 00003 # 00004 # FILE: Classification.php 00005 # AUTHOR: Edward Almasy 00006 # 00007 # Part of the Scout Portal Toolkit 00008 # Copyright 2002-2003 Internet Scout Project 00009 # http://scout.wisc.edu 00010 # 00011 00015 class Classification { 00016 00017 # ---- PUBLIC INTERFACE -------------------------------------------------- 00018 00019 # error status codes 00020 const CLASSSTAT_OK = 0; 00021 const CLASSSTAT_INVALIDID = 1; 00022 const CLASSSTAT_INVALIDPARENTID = 2; 00023 const CLASSSTAT_DUPLICATENAME = 3; 00024 00036 function Classification($ClassId, $Name = NULL, $FieldId = NULL, $ParentId = NULL) 00037 { 00038 static $IdCache; 00039 00040 # assume everything will turn out okay 00041 $this->ErrorStatus = Classification::CLASSSTAT_OK; 00042 00043 # create DB handle for our use 00044 $this->DB = new SPTDatabase(); 00045 $DB = $this->DB; 00046 00047 # if class ID not given (indicating class must be created) 00048 if ($ClassId === NULL) 00049 { 00050 # if parent class supplied 00051 if ($ParentId !== NULL) 00052 { 00053 # if parent ID was invalid 00054 if (($ParentId != -1) 00055 && ($DB->Query("SELECT COUNT(*) AS NumberFound" 00056 ." FROM Classifications" 00057 ." WHERE ClassificationId = ".intval($ParentId), 00058 "NumberFound") < 1)) 00059 { 00060 # set error code for bad parent ID 00061 $this->ErrorStatus = Classification::CLASSSTAT_INVALIDPARENTID; 00062 } 00063 else 00064 { 00065 # if name already exists 00066 $Name = trim($Name); 00067 if ($FieldId === NULL) 00068 { 00069 # If we know what field we're trying to add a classifcation for, 00070 # Check just within that field 00071 $Count = $DB->Query("SELECT COUNT(*) AS NumberFound FROM Classifications" 00072 ." WHERE ParentId = ".intval($ParentId) 00073 ." AND LOWER(SegmentName) = '" 00074 .addslashes(strtolower($Name))."'", 00075 "NumberFound"); 00076 } 00077 else 00078 { 00079 # Otherwise, check all classifications for all fields 00080 $Count = $DB->Query("SELECT COUNT(*) AS NumberFound FROM Classifications" 00081 ." WHERE ParentId = ".intval($ParentId) 00082 ." AND FieldId = ".intval($FieldId) 00083 ." AND LOWER(SegmentName) = '" 00084 .addslashes(strtolower($Name))."'", 00085 "NumberFound"); 00086 } 00087 00088 if ($Count > 0) 00089 { 00090 # set error code for duplicate class name 00091 $this->ErrorStatus = Classification::CLASSSTAT_DUPLICATENAME; 00092 } 00093 else 00094 { 00095 # add class to database 00096 $ParentId = intval($ParentId); 00097 if ($ParentId == -1) 00098 { 00099 $NewName = $Name; 00100 $NewDepth = 0; 00101 } 00102 else 00103 { 00104 $DB->Query("SELECT ClassificationName, Depth" 00105 ." FROM Classifications" 00106 ." WHERE ClassificationId = ".$ParentId); 00107 $ParentInfo = $DB->FetchRow(); 00108 $NewName = $ParentInfo["ClassificationName"]." -- ".$Name; 00109 $NewDepth = $ParentInfo["Depth"] + 1; 00110 } 00111 $DB->Query("INSERT INTO Classifications" 00112 ." (FieldId, ParentId, SegmentName, ResourceCount," 00113 ." Depth, ClassificationName) VALUES" 00114 ." (".intval($FieldId).", ".$ParentId."," 00115 ." '".addslashes($Name)."', 0, " 00116 .$NewDepth.", '".addslashes($NewName)."')"); 00117 00118 # retrieve ID of new class 00119 $this->Id = $DB->LastInsertId("Classifications"); 00120 } 00121 } 00122 } 00123 else 00124 { 00125 # parse classification name into separate segments 00126 $Segments = preg_split("/--/", $Name); 00127 00128 # start out with top as parent 00129 $ParentId = -1; 00130 00131 # for each segment 00132 $CurrentDepth = -1; 00133 $CurrentFullName = ""; 00134 foreach ($Segments as $Segment) 00135 { 00136 # track segment depth and full classification name for use in adding new entries 00137 $Segment = trim($Segment); 00138 $CurrentDepth++; 00139 $CurrentFullName .= (($CurrentFullName == "") ? "" : " -- ").$Segment; 00140 00141 # if we have added classifications 00142 $Segment = addslashes($Segment); 00143 if ($this->SegmentsCreated) 00144 { 00145 # we know that current segment will not be found 00146 $ClassId = NULL; 00147 } 00148 else 00149 { 00150 # look up classification with current parent and segment name 00151 if (!isset($IdCache[$FieldId][$ParentId][$Segment])) 00152 { 00153 if ($ParentId == -1) 00154 { 00155 $IdCache[$FieldId][$ParentId][$Segment] = $DB->Query( 00156 "SELECT ClassificationId FROM Classifications" 00157 ." WHERE ParentId = -1" 00158 ." AND SegmentName = '".addslashes($Segment)."'" 00159 ." AND FieldId = ".intval($FieldId), 00160 "ClassificationId"); 00161 } 00162 else 00163 { 00164 $IdCache[$FieldId][$ParentId][$Segment] = $DB->Query( 00165 "SELECT ClassificationId FROM Classifications " 00166 ."WHERE ParentId = ".intval($ParentId) 00167 ." AND SegmentName = '".addslashes($Segment)."'", 00168 "ClassificationId"); 00169 } 00170 } 00171 $ClassId = $IdCache[$FieldId][$ParentId][$Segment]; 00172 } 00173 00174 # if classification not found 00175 if ($ClassId === NULL) 00176 { 00177 # add new classification 00178 $DB->Query("INSERT INTO Classifications " 00179 ."(FieldId, ParentId, SegmentName," 00180 ." ClassificationName, Depth, ResourceCount) " 00181 ."VALUES (".intval($FieldId).", " 00182 .intval($ParentId).", " 00183 ."'".addslashes($Segment)."', " 00184 ."'".addslashes($CurrentFullName)."', " 00185 .intval($CurrentDepth).", 0)"); 00186 $ClassId = $DB->LastInsertId("Classifications"); 00187 $IdCache[$FieldId][$ParentId][$Segment] = $ClassId; 00188 00189 # track total number of new classification segments created 00190 $this->SegmentsCreated++; 00191 } 00192 00193 # set parent to created or found class 00194 $PreviousParentId = $ParentId; 00195 $ParentId = $ClassId; 00196 } 00197 00198 # our class ID is the one that was last found 00199 $this->Id = $ClassId; 00200 } 00201 } 00202 else 00203 { 00204 # our class ID is the one that was supplied by caller 00205 $this->Id = intval($ClassId); 00206 } 00207 00208 # if no error encountered 00209 if ($this->ErrorStatus == Classification::CLASSSTAT_OK) 00210 { 00211 # load in attributes from database 00212 $DB->Query("SELECT * FROM Classifications" 00213 ." WHERE ClassificationId = ".intval($this->Id)); 00214 $this->DBFields = $DB->NumRowsSelected()>0 ? $DB->FetchRow() : NULL ; 00215 00216 # set error status if class info not loaded 00217 if ($this->DBFields === NULL || 00218 $this->DBFields["ClassificationId"] != $this->Id) 00219 { 00220 $this->ErrorStatus = Classification::CLASSSTAT_INVALIDID; 00221 } 00222 } 00223 } 00224 00229 function Status() { return $this->ErrorStatus; } 00230 00235 function Id() { return $this->Id; } 00236 00241 function FullName() { return stripslashes($this->DBFields["ClassificationName"]); } 00242 00247 function Name() { return $this->FullName(); } 00248 00253 function VariantName() { return NULL; } 00254 00259 function Depth() { return $this->DBFields["Depth"]; } 00260 00266 function ResourceCount() { return $this->DBFields["ResourceCount"]; } 00267 00272 function SegmentsCreated() { return $this->SegmentsCreated; } 00273 00278 function ParentId() { return $this->DBFields["ParentId"]; } 00279 00285 function SegmentName($NewValue = DB_NOVALUE) { 00286 return stripslashes($this->UpdateValue("SegmentName", $NewValue)); } 00287 00295 function LinkString($NewValue = DB_NOVALUE) { 00296 return stripslashes($this->UpdateValue("LinkString", $NewValue)); } 00297 00304 function QualifierId($NewValue = DB_NOVALUE) { 00305 return $this->UpdateValue("QualifierId", $NewValue); } 00306 00312 function FieldId($NewValue = DB_NOVALUE) { 00313 return $this->UpdateValue("FieldId", $NewValue); } 00314 00321 function Qualifier($NewValue = DB_NOVALUE) 00322 { 00323 # if new qualifier supplied 00324 if ($NewValue !== DB_NOVALUE) 00325 { 00326 # set new qualifier ID 00327 $this->QualifierId($NewValue->Id()); 00328 00329 # use new qualifier for return value 00330 $Qualifier = $NewValue; 00331 } 00332 else 00333 { 00334 # if qualifier is available 00335 if ($this->QualifierId() !== NULL) 00336 { 00337 # create qualifier object using stored ID 00338 $Qualifier = new Qualifier($this->QualifierId()); 00339 } 00340 else 00341 { 00342 # return NULL to indicate no qualifier 00343 $Qualifier = NULL; 00344 } 00345 } 00346 00347 # return qualifier to caller 00348 return $Qualifier; 00349 } 00350 00356 function RecalcDepthAndFullName() 00357 { 00358 $DB = $this->DB; 00359 00360 # start with full classification name set to our segment name 00361 $FullClassName = $this->DBFields["SegmentName"]; 00362 00363 # assume to begin with that we're at the top of the hierarchy 00364 $Depth = 0; 00365 00366 # while parent available 00367 $ParentId = $this->DBFields["ParentId"]; 00368 while ($ParentId != -1) 00369 { 00370 # retrieve classification information 00371 $DB->Query("SELECT SegmentName, ParentId " 00372 ."FROM Classifications " 00373 ."WHERE ClassificationId=".$ParentId); 00374 $Record = $DB->FetchRow(); 00375 00376 # prepend segment name to full classification name 00377 $FullClassName = stripslashes($Record["SegmentName"]) 00378 ." -- ".$FullClassName; 00379 00380 # increment depth value 00381 $Depth++; 00382 00383 # move to parent of current classification 00384 $ParentId = $Record["ParentId"]; 00385 } 00386 00387 # for each child 00388 $DB->Query("SELECT ClassificationId " 00389 ."FROM Classifications " 00390 ."WHERE ParentId=".intval($this->Id)); 00391 while ($Record = $DB->FetchRow()) 00392 { 00393 # perform depth and name recalc 00394 $Child = new Classification($Record["ClassificationId"]); 00395 $Child->RecalcDepthAndFullName(); 00396 } 00397 00398 # save new depth and full classification name 00399 $DB->Query("UPDATE Classifications SET " 00400 ."Depth=".intval($Depth).", " 00401 ."ClassificationName='".addslashes($FullClassName)."' " 00402 ."WHERE ClassificationId=".intval($this->Id)); 00403 $this->DBFields["ClassificationName"] = $FullClassName; 00404 $this->DBFields["Depth"] = $Depth; 00405 } 00406 00414 function RecalcResourceCount($IdsToSkip = NULL) 00415 { 00416 $IdsUpdated = array(); 00417 00418 # if we don't have a skip list or we aren't in the skip list 00419 if (!$IdsToSkip || !in_array($this->Id, $IdsToSkip)) 00420 { 00421 # retrieve new count of resources directly associated with class 00422 $this->DB->Query("SELECT COUNT(*) AS ResourceCount" 00423 ." FROM ResourceClassInts, Resources" 00424 ." WHERE ClassificationId=".intval($this->Id) 00425 ." AND ResourceClassInts.ResourceId = Resources.ResourceId" 00426 ." AND ReleaseFlag = 1"); 00427 $Record = $this->DB->FetchRow(); 00428 $ResourceCount = $Record["ResourceCount"]; 00429 00430 # add on resources associated with all children 00431 $ResourceCount += $this->DB->Query( 00432 "SELECT SUM(ResourceCount) AS ResourceCountTotal " 00433 ."FROM Classifications " 00434 ."WHERE ParentId = ".intval($this->Id), 00435 "ResourceCountTotal"); 00436 00437 # save new count to database 00438 $this->DB->Query("UPDATE Classifications SET " 00439 ."ResourceCount=".$ResourceCount." " 00440 ."WHERE ClassificationId=".intval($this->Id)); 00441 00442 # save new count to our local cache 00443 $this->DBFields["ResourceCount"] = $ResourceCount; 00444 00445 # add our ID to list of IDs that have been recalculated 00446 $IdsUpdated[] = $this->Id; 00447 } 00448 00449 # update resource count for our parent (if any) 00450 if (($this->DBFields["ParentId"] != -1) 00451 && (!$IdsToSkip || !in_array($this->DBFields["ParentId"], $IdsToSkip)) ) 00452 { 00453 $Class = new Classification($this->DBFields["ParentId"]); 00454 if ($Class->Status() == Classification::CLASSSTAT_OK) 00455 { 00456 $IdsUpdated = array_merge($IdsUpdated, $Class->RecalcResourceCount()); 00457 } 00458 } 00459 00460 # return list of IDs of updated classifications to caller 00461 return $IdsUpdated; 00462 } 00463 00468 function ChildCount() 00469 { 00470 # return count of classifications that have this one as parent 00471 return $this->DB->Query("SELECT COUNT(*) AS ClassCount " 00472 ."FROM Classifications " 00473 ."WHERE ParentId=".intval($this->Id), 00474 "ClassCount"); 00475 } 00476 00482 # this also returns grandchildren, great grandchildren, etc. 00483 function ChildList() 00484 { 00485 $ChildList = array(); 00486 00487 $this->DB->Query("SELECT ClassificationId " 00488 ."FROM Classifications " 00489 ."WHERE ParentId=".intval($this->Id)); 00490 00491 while ($Entry = $this->DB->FetchRow()) 00492 { 00493 $ChildList[] = $Entry["ClassificationId"]; 00494 $Child = new Classification($Entry["ClassificationId"]); 00495 if($Child->ChildCount() > 0) 00496 { 00497 $GrandChildList = $Child->ChildList(); 00498 $ChildList = array_merge($GrandChildList, $ChildList); 00499 } 00500 } 00501 return $ChildList; 00502 } 00503 00510 function Delete($DeleteParents = FALSE, 00511 $DeleteIfHasResources = FALSE, $DeleteIfHasChildren = FALSE) 00512 { 00513 $DB = $this->DB; 00514 00515 # if no resources or okay to delete with resources 00516 # and no children or okay to delete with children 00517 if (($DeleteIfHasResources || ($this->ResourceCount() == 0)) 00518 && ($DeleteIfHasChildren || ($this->ChildCount() == 0))) 00519 { 00520 $ParentId = $this->DBFields["ParentId"]; 00521 00522 if ($DeleteIfHasResources) 00523 { 00524 $DB->Query("DELETE FROM ResourceClassInts " 00525 ."WHERE ClassificationId=".intval($this->Id)); 00526 $this->RecalcResourceCount(); 00527 } 00528 # delete this classification 00529 $DB->Query("DELETE FROM Classifications " 00530 ."WHERE ClassificationId=".intval($this->Id)); 00531 00532 # delete parent classification (if requested) 00533 if (($DeleteParents) && ($this->DBFields["ParentId"] != -1)) 00534 { 00535 $Parent = new Classification($this->DBFields["ParentId"]); 00536 $Parent->Delete( 00537 TRUE, $DeleteIfHasResources, $DeleteIfHasChildren); 00538 } 00539 } 00540 } 00541 00542 00543 # ---- PRIVATE INTERFACE ------------------------------------------------- 00544 00545 private $DB; 00546 private $DBFields; 00547 private $Id; 00548 private $ErrorStatus; 00549 private $SegmentsCreated; 00550 00551 # convenience function to supply parameters to Database->UpdateValue() 00552 private function UpdateValue($FieldName, $NewValue) 00553 { 00554 return $this->DB->UpdateValue("Classifications", $FieldName, $NewValue, 00555 "ClassificationId = ".intval($this->Id), 00556 $this->DBFields, TRUE); 00557 } 00558 } 00559 00560 00561 ?>