CWIS Developer Documentation
Classification.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # FILE: Classification.php
4 #
5 # Part of the Collection Workflow Integration System (CWIS)
6 # Copyright 2002-2013 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu/cwis/
8 #
9 
14 
15  # ---- PUBLIC INTERFACE --------------------------------------------------
16 
18  const CLASSSTAT_OK = 0;
25 
42  function Classification($ClassId, $Name = NULL, $FieldId = NULL, $ParentId = NULL)
43  {
44  static $IdCache;
45 
46  # assume everything will turn out okay
47  $this->ErrorStatus = self::CLASSSTAT_OK;
48 
49  # create DB handle for our use
50  $this->DB = new Database();
51  $DB = $this->DB;
52 
53  # if class ID not given (indicating class must be created)
54  if ($ClassId === NULL)
55  {
56  # if parent class supplied
57  if ($ParentId !== NULL)
58  {
59  # if parent ID was invalid
60  if (($ParentId != -1)
61  && ($DB->Query("SELECT COUNT(*) AS NumberFound"
62  ." FROM Classifications"
63  ." WHERE ClassificationId = ".intval($ParentId),
64  "NumberFound") < 1))
65  {
66  # set error code for bad parent ID
67  $this->ErrorStatus = self::CLASSSTAT_INVALIDPARENTID;
68  }
69  else
70  {
71  # if name already exists
72  $Name = trim($Name);
73  if ($FieldId === NULL)
74  {
75  # If we know what field we're trying to add a classifcation for,
76  # Check just within that field
77  $Count = $DB->Query("SELECT COUNT(*) AS NumberFound FROM Classifications"
78  ." WHERE ParentId = ".intval($ParentId)
79  ." AND LOWER(SegmentName) = '"
80  .addslashes(strtolower($Name))."'",
81  "NumberFound");
82  }
83  else
84  {
85  # Otherwise, check all classifications for all fields
86  $Count = $DB->Query("SELECT COUNT(*) AS NumberFound FROM Classifications"
87  ." WHERE ParentId = ".intval($ParentId)
88  ." AND FieldId = ".intval($FieldId)
89  ." AND LOWER(SegmentName) = '"
90  .addslashes(strtolower($Name))."'",
91  "NumberFound");
92  }
93 
94  if ($Count > 0)
95  {
96  # set error code for duplicate class name
97  $this->ErrorStatus = self::CLASSSTAT_DUPLICATENAME;
98  }
99  else
100  {
101  # add class to database
102  $ParentId = intval($ParentId);
103  if ($ParentId == -1)
104  {
105  $NewName = $Name;
106  $NewDepth = 0;
107  }
108  else
109  {
110  $DB->Query("SELECT ClassificationName, Depth"
111  ." FROM Classifications"
112  ." WHERE ClassificationId = ".$ParentId);
113  $ParentInfo = $DB->FetchRow();
114  $NewName = $ParentInfo["ClassificationName"]." -- ".$Name;
115  $NewDepth = $ParentInfo["Depth"] + 1;
116  }
117  $DB->Query("INSERT INTO Classifications"
118  ." (FieldId, ParentId, SegmentName, ResourceCount,"
119  ." Depth, ClassificationName) VALUES"
120  ." (".intval($FieldId).", ".$ParentId.","
121  ." '".addslashes($Name)."', 0, "
122  .$NewDepth.", '".addslashes($NewName)."')");
123 
124  # retrieve ID of new class
125  $this->Id = $DB->LastInsertId();
126  }
127  }
128  }
129  else
130  {
131  # parse classification name into separate segments
132  $Segments = preg_split("/--/", $Name);
133 
134  # start out with top as parent
135  $ParentId = -1;
136 
137  # for each segment
138  $CurrentDepth = -1;
139  $CurrentFullName = "";
140  foreach ($Segments as $Segment)
141  {
142  # track segment depth and full classification name for use in adding new entries
143  $Segment = trim($Segment);
144  $CurrentDepth++;
145  $CurrentFullName .= (($CurrentFullName == "") ? "" : " -- ").$Segment;
146 
147  # if we have added classifications
148  $Segment = addslashes($Segment);
149  if ($this->SegmentsCreated)
150  {
151  # we know that current segment will not be found
152  $ClassId = NULL;
153  }
154  else
155  {
156  # look up classification with current parent and segment name
157  if (!isset($IdCache[$FieldId][$ParentId][$Segment]))
158  {
159  if ($ParentId == -1)
160  {
161  $IdCache[$FieldId][$ParentId][$Segment] = $DB->Query(
162  "SELECT ClassificationId FROM Classifications"
163  ." WHERE ParentId = -1"
164  ." AND SegmentName = '".addslashes($Segment)."'"
165  ." AND FieldId = ".intval($FieldId),
166  "ClassificationId");
167  }
168  else
169  {
170  $IdCache[$FieldId][$ParentId][$Segment] = $DB->Query(
171  "SELECT ClassificationId FROM Classifications "
172  ."WHERE ParentId = ".intval($ParentId)
173  ." AND SegmentName = '".addslashes($Segment)."'",
174  "ClassificationId");
175  }
176  }
177  $ClassId = $IdCache[$FieldId][$ParentId][$Segment];
178  }
179 
180  # if classification not found
181  if ($ClassId === NULL)
182  {
183  # add new classification
184  $DB->Query("INSERT INTO Classifications "
185  ."(FieldId, ParentId, SegmentName,"
186  ." ClassificationName, Depth, ResourceCount) "
187  ."VALUES (".intval($FieldId).", "
188  .intval($ParentId).", "
189  ."'".addslashes($Segment)."', "
190  ."'".addslashes($CurrentFullName)."', "
191  .intval($CurrentDepth).", 0)");
192  $ClassId = $DB->LastInsertId();
193  $IdCache[$FieldId][$ParentId][$Segment] = $ClassId;
194 
195  # track total number of new classification segments created
196  $this->SegmentsCreated++;
197  }
198 
199  # set parent to created or found class
200  $PreviousParentId = $ParentId;
201  $ParentId = $ClassId;
202  }
203 
204  # our class ID is the one that was last found
205  $this->Id = $ClassId;
206  }
207  }
208  else
209  {
210  # our class ID is the one that was supplied by caller
211  $this->Id = intval($ClassId);
212  }
213 
214  # if no error encountered
215  if ($this->ErrorStatus == self::CLASSSTAT_OK)
216  {
217  # load in attributes from database
218  $DB->Query("SELECT * FROM Classifications"
219  ." WHERE ClassificationId = ".intval($this->Id));
220  $this->DBFields = $DB->NumRowsSelected()>0 ? $DB->FetchRow() : NULL ;
221 
222  # set error status if class info not loaded
223  if ($this->DBFields === NULL ||
224  $this->DBFields["ClassificationId"] != $this->Id)
225  {
226  $this->ErrorStatus = self::CLASSSTAT_INVALIDID;
227  }
228  }
229  }
230 
235  function Status() { return $this->ErrorStatus; }
236 
241  function Id() { return $this->Id; }
242 
247  function FullName()
248  {
249  return $this->DBFields["ClassificationName"];
250  }
251 
256  function Name() { return $this->FullName(); }
257 
262  function VariantName() { return NULL; }
263 
268  function Depth() { return $this->DBFields["Depth"]; }
269 
275  function ResourceCount() { return $this->DBFields["ResourceCount"]; }
276 
283  function FullResourceCount() { return $this->DBFields["FullResourceCount"]; }
284 
289  function SegmentsCreated() { return $this->SegmentsCreated; }
290 
295  function ParentId() { return $this->DBFields["ParentId"]; }
296 
302  function SegmentName($NewValue = DB_NOVALUE) {
303  return $this->UpdateValue("SegmentName", $NewValue); }
304 
312  function LinkString($NewValue = DB_NOVALUE) {
313  return $this->UpdateValue("LinkString", $NewValue); }
314 
321  function QualifierId($NewValue = DB_NOVALUE) {
322  return $this->UpdateValue("QualifierId", $NewValue); }
323 
329  function FieldId($NewValue = DB_NOVALUE) {
330  return $this->UpdateValue("FieldId", $NewValue); }
331 
338  function Qualifier($NewValue = DB_NOVALUE)
339  {
340  # if new qualifier supplied
341  if ($NewValue !== DB_NOVALUE)
342  {
343  # set new qualifier ID
344  $this->QualifierId($NewValue->Id());
345 
346  # use new qualifier for return value
347  $Qualifier = $NewValue;
348  }
349  else
350  {
351  # if qualifier is available
352  if ($this->QualifierId() !== NULL)
353  {
354  # create qualifier object using stored ID
355  $Qualifier = new Qualifier($this->QualifierId());
356  }
357  else
358  {
359  # return NULL to indicate no qualifier
360  $Qualifier = NULL;
361  }
362  }
363 
364  # return qualifier to caller
365  return $Qualifier;
366  }
367 
374  {
375  $DB = $this->DB;
376 
377  # start with full classification name set to our segment name
378  $FullClassName = $this->DBFields["SegmentName"];
379 
380  # assume to begin with that we're at the top of the hierarchy
381  $Depth = 0;
382 
383  # while parent available
384  $ParentId = $this->DBFields["ParentId"];
385  while ($ParentId != -1)
386  {
387  # retrieve classification information
388  $DB->Query("SELECT SegmentName, ParentId "
389  ."FROM Classifications "
390  ."WHERE ClassificationId=".$ParentId);
391  $Record = $DB->FetchRow();
392 
393  # prepend segment name to full classification name
394  $FullClassName = $Record["SegmentName"]." -- ".$FullClassName;
395 
396  # increment depth value
397  $Depth++;
398 
399  # move to parent of current classification
400  $ParentId = $Record["ParentId"];
401  }
402 
403  # for each child
404  $DB->Query("SELECT ClassificationId "
405  ."FROM Classifications "
406  ."WHERE ParentId=".intval($this->Id));
407  while ($Record = $DB->FetchRow())
408  {
409  # perform depth and name recalc
410  $Child = new Classification($Record["ClassificationId"]);
411  $Child->RecalcDepthAndFullName();
412  }
413 
414  # save new depth and full classification name
415  $DB->Query("UPDATE Classifications SET "
416  ."Depth=".intval($Depth).", "
417  ."ClassificationName='".addslashes($FullClassName)."' "
418  ."WHERE ClassificationId=".intval($this->Id));
419  $this->DBFields["ClassificationName"] = $FullClassName;
420  $this->DBFields["Depth"] = $Depth;
421  }
422 
427  {
428  $this->DB->Query("UPDATE Classifications SET LastAssigned=NOW() "
429  ."WHERE ClassificationId=".intval($this->Id));
430  }
431 
439  function RecalcResourceCount($IdsToSkip = NULL)
440  {
441  $IdsUpdated = array();
442 
443  # if we don't have a skip list or we aren't in the skip list
444  if (!$IdsToSkip || !in_array($this->Id, $IdsToSkip))
445  {
446  # retrieve new count of resources directly associated with class
447  $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
448  ." FROM ResourceClassInts, Resources"
449  ." WHERE ClassificationId=".intval($this->Id)
450  ." AND ResourceClassInts.ResourceId = Resources.ResourceId"
451  ." AND Resources.ResourceId > 0"
452  ." AND ReleaseFlag = 1");
453  $Record = $this->DB->FetchRow();
454  $ResourceCount = $Record["ResourceCount"];
455 
456  # add on resources associated with all children
457  $ResourceCount += $this->DB->Query(
458  "SELECT SUM(ResourceCount) AS ResourceCountTotal "
459  ."FROM Classifications "
460  ."WHERE ParentId = ".intval($this->Id),
461  "ResourceCountTotal");
462 
463  # save new count to database
464  $this->DB->Query("UPDATE Classifications SET "
465  ."ResourceCount=".$ResourceCount." "
466  ."WHERE ClassificationId=".intval($this->Id));
467 
468  # save new count to our local cache
469  $this->DBFields["ResourceCount"] = $ResourceCount;
470 
471  # add our ID to list of IDs that have been recalculated
472  $IdsUpdated[] = $this->Id;
473  }
474 
475  # update resource count for our parent (if any)
476  if (($this->DBFields["ParentId"] != -1)
477  && (!$IdsToSkip || !in_array($this->DBFields["ParentId"], $IdsToSkip)) )
478  {
479  $Class = new Classification($this->DBFields["ParentId"]);
480  if ($Class->Status() == self::CLASSSTAT_OK)
481  {
482  $IdsUpdated = array_merge($IdsUpdated, $Class->RecalcResourceCount());
483  }
484  }
485 
486  # retrieve new count of all resources directly associated with class
487  $FullCount = $this->DB->Query("
488  SELECT COUNT(*) AS ResourceCount
489  FROM ResourceClassInts I, Resources R
490  WHERE I.ClassificationId = '".intval($this->Id)."'
491  AND R.ResourceId > 0
492  AND I.ResourceId = R.ResourceId",
493  "ResourceCount");
494 
495  # add on resources associated with all children
496  $FullCount += $this->DB->Query("
497  SELECT SUM(ResourceCount) AS ResourceCountTotal
498  FROM Classifications
499  WHERE ParentId = '".intval($this->Id)."'",
500  "ResourceCountTotal");
501 
502  # save new count to database
503  $this->DB->Query("
504  UPDATE Classifications
505  SET FullResourceCount = '".intval($FullCount)."'
506  WHERE ClassificationId = '".intval($this->Id)."'");
507 
508  # save new count to our local cache
509  $this->DBFields["FullResourceCount"] = $FullCount;
510 
511  # return list of IDs of updated classifications to caller
512  return $IdsUpdated;
513  }
514 
519  function ChildCount()
520  {
521  # return count of classifications that have this one as parent
522  return $this->DB->Query("SELECT COUNT(*) AS ClassCount "
523  ."FROM Classifications "
524  ."WHERE ParentId=".intval($this->Id),
525  "ClassCount");
526  }
527 
533  function ChildList()
534  {
535  $ChildList = array();
536 
537  $this->DB->Query("SELECT ClassificationId "
538  ."FROM Classifications "
539  ."WHERE ParentId=".intval($this->Id));
540 
541  while ($Entry = $this->DB->FetchRow())
542  {
543  $ChildList[] = $Entry["ClassificationId"];
544  $Child = new Classification($Entry["ClassificationId"]);
545  if($Child->ChildCount() > 0)
546  {
547  $GrandChildList = $Child->ChildList();
548  $ChildList = array_merge($GrandChildList, $ChildList);
549  }
550  }
551  return array_unique($ChildList);
552  }
553 
566  function Delete($DeleteParents = FALSE,
567  $DeleteIfHasResources = FALSE, $DeleteIfHasChildren = FALSE)
568  {
569  $DB = $this->DB;
570 
571  # if no resources or okay to delete with resources
572  # and no children or okay to delete with children
573  if (($DeleteIfHasResources || ($this->ResourceCount() == 0))
574  && ($DeleteIfHasChildren || ($this->ChildCount() == 0)))
575  {
576  $ParentId = $this->DBFields["ParentId"];
577 
578  if ($DeleteIfHasResources)
579  {
580  $DB->Query("DELETE FROM ResourceClassInts "
581  ."WHERE ClassificationId=".intval($this->Id));
582  $this->RecalcResourceCount();
583  }
584  # delete this classification
585  $DB->Query("DELETE FROM Classifications "
586  ."WHERE ClassificationId=".intval($this->Id));
587 
588  # delete parent classification (if requested)
589  if (($DeleteParents) && ($this->DBFields["ParentId"] != -1))
590  {
591  $Parent = new Classification($this->DBFields["ParentId"]);
592  $Parent->Delete(
593  TRUE, $DeleteIfHasResources, $DeleteIfHasChildren);
594  }
595  }
596  }
597 
598 
599  # ---- PRIVATE INTERFACE -------------------------------------------------
600 
601  private $DB;
602  private $DBFields;
603  private $Id;
604  private $ErrorStatus;
605  private $SegmentsCreated;
606 
614  private function UpdateValue($FieldName, $NewValue)
615  {
616  return $this->DB->UpdateValue("Classifications", $FieldName, $NewValue,
617  "ClassificationId = ".intval($this->Id),
618  $this->DBFields, TRUE);
619  }
620 }
LinkString($NewValue=DB_NOVALUE)
Get or set the stored link string for the Classification.
const CLASSSTAT_INVALIDPARENTID
Status code indicating an invalid parent classification ID was specified.
FullName()
Get full classification name (all segments).
FullResourceCount()
Get number of all resources (minus temporary ones) having this classification assigned to them...
SQL database abstraction object with smart query caching.
const CLASSSTAT_INVALIDID
Status code indicating an invalid classification ID was specified.
const DB_NOVALUE
FieldId($NewValue=DB_NOVALUE)
Get or set the ID of the MetadataField for the Classification.
VariantName()
Get variant name of classification, if any.
Qualifier($NewValue=DB_NOVALUE)
Get or set the Qualifier associated with the Classification.
ChildCount()
Get number of classifications that have this Classification as their direct parent.
Classification($ClassId, $Name=NULL, $FieldId=NULL, $ParentId=NULL)
Class constructor.
RecalcResourceCount($IdsToSkip=NULL)
Recalculate number of resources assigned to class and any parent classes.
Name()
Get name of classification segment.
ParentId()
Get ID of parent Classification.
SegmentsCreated()
Get number of new segments (Classifications) generated when creating a new Classification with a full...
RecalcDepthAndFullName()
Rebuild classification full name and recalculate depth in hierarchy.
Status()
Returns success/failure code for last call (where applicable).
Depth()
Get depth of classification in hierarchy.
const CLASSSTAT_DUPLICATENAME
Status code indicating a duplicate classification name was specified.
ResourceCount()
Get number of released resources having this classification assigned to them.
Delete($DeleteParents=FALSE, $DeleteIfHasResources=FALSE, $DeleteIfHasChildren=FALSE)
Remove Classification (and accompanying associations) from database.
const CLASSSTAT_OK
Status code indicating operation completed successfully.
UpdateLastAssigned()
Update the LastAssigned timestamp for this classification.
Metadata type representing hierarchical ("Tree") controlled vocabulary values.
QualifierId($NewValue=DB_NOVALUE)
Get or set the Qualifier associated with the Classification by ID.
ChildList()
Get list of IDs of Classifications that have this class as an "ancestor" (parent, grandparent...
SegmentName($NewValue=DB_NOVALUE)
Get or set the segment name.
Id()
Get Classification ID.