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 
16  # ---- PUBLIC INTERFACE --------------------------------------------------
17 
19  const CLASSSTAT_OK = 0;
26 
43  public function __construct($ClassId, $Name = NULL, $FieldId = NULL, $ParentId = NULL)
44  {
45  static $IdCache;
46 
47  # assume everything will turn out okay
48  $this->ErrorStatus = self::CLASSSTAT_OK;
49 
50  # create DB handle for our use
51  $this->DB = new Database();
52  $DB = $this->DB;
53 
54  # if class ID not given (indicating class must be created)
55  if ($ClassId === NULL)
56  {
57  if($FieldId === NULL)
58  {
59  throw new Exception("Cannot create a new
60  classification without specifying a field.");
61  }
62  # if parent class supplied
63  if ($ParentId !== NULL)
64  {
65  # if parent ID was invalid
66  if (($ParentId != -1)
67  && ($DB->Query("SELECT COUNT(*) AS NumberFound"
68  ." FROM Classifications"
69  ." WHERE ClassificationId = ".intval($ParentId),
70  "NumberFound") < 1))
71  {
72  # set error code for bad parent ID
73  $this->ErrorStatus = self::CLASSSTAT_INVALIDPARENTID;
74  }
75  else
76  {
77  # if name already exists
78  $Name = trim($Name);
79  if ($FieldId === NULL)
80  {
81  # If we know what field we're trying to add a classifcation for,
82  # Check just within that field
83  $Count = $DB->Query("SELECT COUNT(*) AS NumberFound"
84  ." FROM Classifications"
85  ." WHERE ParentId = ".intval($ParentId)
86  ." AND LOWER(SegmentName) = '"
87  .addslashes(strtolower($Name))."'",
88  "NumberFound");
89  }
90  else
91  {
92  # Otherwise, check all classifications for all fields
93  $Count = $DB->Query("SELECT COUNT(*) AS NumberFound"
94  ." FROM Classifications"
95  ." WHERE ParentId = ".intval($ParentId)
96  ." AND FieldId = ".intval($FieldId)
97  ." AND LOWER(SegmentName) = '"
98  .addslashes(strtolower($Name))."'",
99  "NumberFound");
100  }
101 
102  if ($Count > 0)
103  {
104  # set error code for duplicate class name
105  $this->ErrorStatus = self::CLASSSTAT_DUPLICATENAME;
106  }
107  else
108  {
109  # add class to database
110  $ParentId = intval($ParentId);
111  if ($ParentId == -1)
112  {
113  $NewName = $Name;
114  $NewDepth = 0;
115  }
116  else
117  {
118  $DB->Query("SELECT ClassificationName, Depth"
119  ." FROM Classifications"
120  ." WHERE ClassificationId = ".$ParentId);
121  $ParentInfo = $DB->FetchRow();
122  $NewName = $ParentInfo["ClassificationName"]." -- ".$Name;
123  $NewDepth = $ParentInfo["Depth"] + 1;
124  }
125  $DB->Query("INSERT INTO Classifications"
126  ." (FieldId, ParentId, SegmentName, ResourceCount,"
127  ." Depth, ClassificationName) VALUES"
128  ." (".intval($FieldId).", ".$ParentId.","
129  ." '".addslashes($Name)."', 0, "
130  .$NewDepth.", '".addslashes($NewName)."')");
131 
132  # retrieve ID of new class
133  $this->Id = $DB->LastInsertId();
134  }
135  }
136  }
137  else
138  {
139  # parse classification name into separate segments
140  $Segments = preg_split("/--/", $Name);
141 
142  # start out with top as parent
143  $ParentId = -1;
144 
145  # for each segment
146  $CurrentDepth = -1;
147  $CurrentFullName = "";
148  foreach ($Segments as $Segment)
149  {
150  # track segment depth and full classification name for use
151  # in adding new entries
152  $Segment = trim($Segment);
153  $CurrentDepth++;
154  $CurrentFullName .= (($CurrentFullName == "") ? "" : " -- ").$Segment;
155 
156  # if we have added classifications
157  $Segment = addslashes($Segment);
158  if ($this->SegmentsCreated)
159  {
160  # we know that current segment will not be found
161  $ClassId = NULL;
162  }
163  else
164  {
165  # look up classification with current parent and segment name
166  if (!isset($IdCache[$FieldId][$ParentId][$Segment]))
167  {
168  if ($ParentId == -1)
169  {
170  $IdCache[$FieldId][$ParentId][$Segment] = $DB->Query(
171  "SELECT ClassificationId FROM Classifications"
172  ." WHERE ParentId = -1"
173  ." AND SegmentName = '".addslashes($Segment)."'"
174  ." AND FieldId = ".intval($FieldId),
175  "ClassificationId");
176  }
177  else
178  {
179  $IdCache[$FieldId][$ParentId][$Segment] = $DB->Query(
180  "SELECT ClassificationId FROM Classifications "
181  ."WHERE ParentId = ".intval($ParentId)
182  ." AND SegmentName = '".addslashes($Segment)."'",
183  "ClassificationId");
184  }
185  }
186  $ClassId = $IdCache[$FieldId][$ParentId][$Segment];
187  }
188 
189  # if classification not found
190  if ($ClassId === NULL)
191  {
192  # add new classification
193  $DB->Query("INSERT INTO Classifications "
194  ."(FieldId, ParentId, SegmentName,"
195  ." ClassificationName, Depth, ResourceCount) "
196  ."VALUES (".intval($FieldId).", "
197  .intval($ParentId).", "
198  ."'".addslashes($Segment)."', "
199  ."'".addslashes($CurrentFullName)."', "
200  .intval($CurrentDepth).", 0)");
201  $ClassId = $DB->LastInsertId();
202  $IdCache[$FieldId][$ParentId][$Segment] = $ClassId;
203 
204  # track total number of new classification segments created
205  $this->SegmentsCreated++;
206  }
207 
208  # set parent to created or found class
209  $ParentId = $ClassId;
210  }
211 
212  # our class ID is the one that was last found
213  $this->Id = $ClassId;
214  }
215  }
216  else
217  {
218  # our class ID is the one that was supplied by caller
219  $this->Id = intval($ClassId);
220  }
221 
222  # if no error encountered
223  if ($this->ErrorStatus == self::CLASSSTAT_OK)
224  {
225  # load in attributes from database
226  $DB->Query("SELECT * FROM Classifications"
227  ." WHERE ClassificationId = ".intval($this->Id));
228  $this->DBFields = $DB->NumRowsSelected()>0 ? $DB->FetchRow() : NULL ;
229 
230  # set error status if class info not loaded
231  if ($this->DBFields === NULL ||
232  $this->DBFields["ClassificationId"] != $this->Id)
233  {
234  $this->ErrorStatus = self::CLASSSTAT_INVALIDID;
235  }
236  }
237  }
238 
243  public function Status()
244  {
245  return $this->ErrorStatus;
246  }
247 
252  public function Id()
253  {
254  return $this->Id;
255  }
256 
261  public function FullName()
262  {
263  return $this->DBFields["ClassificationName"];
264  }
265 
270  public function Name()
271  {
272  return $this->FullName();
273  }
274 
279  public function VariantName()
280  {
281  return NULL;
282  }
283 
288  public function Depth()
289  {
290  return $this->DBFields["Depth"];
291  }
292 
298  public function ResourceCount()
299  {
300  return $this->DBFields["ResourceCount"];
301  }
302 
309  public function FullResourceCount()
310  {
311  return $this->DBFields["FullResourceCount"];
312  }
313 
318  public function SegmentsCreated()
319  {
320  return $this->SegmentsCreated;
321  }
322 
327  public function ParentId()
328  {
329  return $this->DBFields["ParentId"];
330  }
331 
337  public function SegmentName($NewValue = DB_NOVALUE)
338  {
339  return $this->UpdateValue("SegmentName", $NewValue);
340  }
341 
349  public function LinkString($NewValue = DB_NOVALUE)
350  {
351  return $this->UpdateValue("LinkString", $NewValue);
352  }
353 
360  public function QualifierId($NewValue = DB_NOVALUE)
361  {
362  return $this->UpdateValue("QualifierId", $NewValue);
363  }
364 
370  public function FieldId($NewValue = DB_NOVALUE)
371  {
372  return $this->UpdateValue("FieldId", $NewValue);
373  }
374 
381  public function Qualifier($NewValue = DB_NOVALUE)
382  {
383  # if new qualifier supplied
384  if ($NewValue !== DB_NOVALUE)
385  {
386  # set new qualifier ID
387  $this->QualifierId($NewValue->Id());
388 
389  # use new qualifier for return value
390  $Qualifier = $NewValue;
391  }
392  else
393  {
394  # if qualifier is available
395  if ($this->QualifierId() !== NULL)
396  {
397  # create qualifier object using stored ID
398  $Qualifier = new Qualifier($this->QualifierId());
399  }
400  else
401  {
402  # return NULL to indicate no qualifier
403  $Qualifier = NULL;
404  }
405  }
406 
407  # return qualifier to caller
408  return $Qualifier;
409  }
410 
416  public function RecalcDepthAndFullName()
417  {
418  $DB = $this->DB;
419 
420  # start with full classification name set to our segment name
421  $FullClassName = $this->DBFields["SegmentName"];
422 
423  # assume to begin with that we're at the top of the hierarchy
424  $Depth = 0;
425 
426  # while parent available
427  $ParentId = $this->DBFields["ParentId"];
428  while ($ParentId != -1)
429  {
430  # retrieve classification information
431  $DB->Query("SELECT SegmentName, ParentId "
432  ."FROM Classifications "
433  ."WHERE ClassificationId=".$ParentId);
434  $Record = $DB->FetchRow();
435 
436  # prepend segment name to full classification name
437  $FullClassName = $Record["SegmentName"]." -- ".$FullClassName;
438 
439  # increment depth value
440  $Depth++;
441 
442  # move to parent of current classification
443  $ParentId = $Record["ParentId"];
444  }
445 
446  # for each child
447  $DB->Query("SELECT ClassificationId "
448  ."FROM Classifications "
449  ."WHERE ParentId=".intval($this->Id));
450  while ($Record = $DB->FetchRow())
451  {
452  # perform depth and name recalc
453  $Child = new Classification($Record["ClassificationId"]);
454  $Child->RecalcDepthAndFullName();
455  }
456 
457  # save new depth and full classification name
458  $DB->Query("UPDATE Classifications SET "
459  ."Depth=".intval($Depth).", "
460  ."ClassificationName='".addslashes($FullClassName)."' "
461  ."WHERE ClassificationId=".intval($this->Id));
462  $this->DBFields["ClassificationName"] = $FullClassName;
463  $this->DBFields["Depth"] = $Depth;
464  }
465 
469  public function UpdateLastAssigned()
470  {
471  $this->DB->Query("UPDATE Classifications SET LastAssigned=NOW() "
472  ."WHERE ClassificationId=".intval($this->Id));
473  }
474 
482  public function RecalcResourceCount($IdsToSkip = NULL)
483  {
484  $IdsUpdated = array();
485 
486  # if we don't have a skip list or we aren't in the skip list
487  if (!$IdsToSkip || !in_array($this->Id, $IdsToSkip))
488  {
489  # retrieve new count of resources directly associated with class
490  $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
491  ." FROM ResourceClassInts, Resources"
492  ." WHERE ClassificationId=".intval($this->Id)
493  ." AND ResourceClassInts.ResourceId = Resources.ResourceId"
494  ." AND Resources.ResourceId > 0"
495  ." AND ReleaseFlag = 1");
496  $Record = $this->DB->FetchRow();
497  $ResourceCount = $Record["ResourceCount"];
498 
499  # add on resources associated with all children
500  $ResourceCount += $this->DB->Query(
501  "SELECT SUM(ResourceCount) AS ResourceCountTotal "
502  ."FROM Classifications "
503  ."WHERE ParentId = ".intval($this->Id),
504  "ResourceCountTotal");
505 
506  # save new count to database
507  $this->DB->Query("UPDATE Classifications SET "
508  ."ResourceCount=".$ResourceCount." "
509  ."WHERE ClassificationId=".intval($this->Id));
510 
511  # save new count to our local cache
512  $this->DBFields["ResourceCount"] = $ResourceCount;
513 
514  # add our ID to list of IDs that have been recalculated
515  $IdsUpdated[] = $this->Id;
516  }
517 
518  # update resource count for our parent (if any)
519  if (($this->DBFields["ParentId"] != -1)
520  && (!$IdsToSkip || !in_array($this->DBFields["ParentId"], $IdsToSkip)) )
521  {
522  $Class = new Classification($this->DBFields["ParentId"]);
523  if ($Class->Status() == self::CLASSSTAT_OK)
524  {
525  $IdsUpdated = array_merge($IdsUpdated, $Class->RecalcResourceCount());
526  }
527  }
528 
529  # retrieve new count of all resources directly associated with class
530  $FullCount = $this->DB->Query("
531  SELECT COUNT(*) AS ResourceCount
532  FROM ResourceClassInts I, Resources R
533  WHERE I.ClassificationId = '".intval($this->Id)."'
534  AND R.ResourceId > 0
535  AND I.ResourceId = R.ResourceId",
536  "ResourceCount");
537 
538  # add on resources associated with all children
539  $FullCount += $this->DB->Query("
540  SELECT SUM(ResourceCount) AS ResourceCountTotal
541  FROM Classifications
542  WHERE ParentId = '".intval($this->Id)."'",
543  "ResourceCountTotal");
544 
545  # save new count to database
546  $this->DB->Query("
547  UPDATE Classifications
548  SET FullResourceCount = '".intval($FullCount)."'
549  WHERE ClassificationId = '".intval($this->Id)."'");
550 
551  # save new count to our local cache
552  $this->DBFields["FullResourceCount"] = $FullCount;
553 
554  # return list of IDs of updated classifications to caller
555  return $IdsUpdated;
556  }
557 
562  public function ChildCount()
563  {
564  # return count of classifications that have this one as parent
565  return $this->DB->Query("SELECT COUNT(*) AS ClassCount "
566  ."FROM Classifications "
567  ."WHERE ParentId=".intval($this->Id),
568  "ClassCount");
569  }
570 
576  public function ChildList()
577  {
578  $ChildList = array();
579 
580  $this->DB->Query("SELECT ClassificationId "
581  ."FROM Classifications "
582  ."WHERE ParentId=".intval($this->Id));
583 
584  while ($Entry = $this->DB->FetchRow())
585  {
586  $ChildList[] = $Entry["ClassificationId"];
587  $Child = new Classification($Entry["ClassificationId"]);
588  if($Child->ChildCount() > 0)
589  {
590  $GrandChildList = $Child->ChildList();
591  $ChildList = array_merge($GrandChildList, $ChildList);
592  }
593  }
594  return array_unique($ChildList);
595  }
596 
609  public function Delete($DeleteParents = FALSE,
610  $DeleteIfHasResources = FALSE, $DeleteIfHasChildren = FALSE)
611  {
612  $DB = $this->DB;
613 
614  # if no resources or okay to delete with resources
615  # and no children or okay to delete with children
616  if (($DeleteIfHasResources || ($this->ResourceCount() == 0))
617  && ($DeleteIfHasChildren || ($this->ChildCount() == 0)))
618  {
619  if ($DeleteIfHasResources)
620  {
621  $DB->Query("DELETE FROM ResourceClassInts "
622  ."WHERE ClassificationId=".intval($this->Id));
623  $this->RecalcResourceCount();
624  }
625  # delete this classification
626  $DB->Query("DELETE FROM Classifications "
627  ."WHERE ClassificationId=".intval($this->Id));
628 
629  # delete parent classification (if requested)
630  $ParentId = $this->DBFields["ParentId"];
631  if (($DeleteParents) && ($ParentId != -1))
632  {
633  $Parent = new Classification($ParentId);
634  $Parent->Delete(
635  TRUE, $DeleteIfHasResources, $DeleteIfHasChildren);
636  }
637  }
638  }
639 
640 
641  # ---- PRIVATE INTERFACE -------------------------------------------------
642 
643  private $DB;
644  private $DBFields;
645  private $Id;
646  private $ErrorStatus;
647  private $SegmentsCreated;
648 
656  private function UpdateValue($FieldName, $NewValue)
657  {
658  return $this->DB->UpdateValue("Classifications", $FieldName, $NewValue,
659  "ClassificationId = ".intval($this->Id),
660  $this->DBFields, TRUE);
661  }
662 }
LinkString($NewValue=DB_NOVALUE)
Get or set the stored link string for the Classification.
__construct($ClassId, $Name=NULL, $FieldId=NULL, $ParentId=NULL)
Class constructor.
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.
Definition: Database.php:22
const CLASSSTAT_INVALIDID
Status code indicating an invalid classification ID was specified.
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.
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.
const DB_NOVALUE
Definition: Database.php:1396
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.