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-2016 Edward Almasy and Internet Scout Research Group
7 # http://scout.wisc.edu/cwis/
8 #
9 
13 class Classification extends Item
14 {
15  # ---- PUBLIC INTERFACE --------------------------------------------------
16 
18  const NOPARENT = -1;
19 
31  public static function Create($Name, $FieldId, $ParentId = NULL)
32  {
33  static $IdCache;
34 
35  # initialize state for creation
36  self::$SegmentsCreated = 0;
37 
38  # if parent class supplied
39  $DB = new Database();
40  if ($ParentId !== NULL)
41  {
42  # error out if parent ID is invalid
43  if ($ParentId != self::NOPARENT)
44  {
45  if ($DB->Query("SELECT COUNT(*) AS NumberFound"
46  ." FROM Classifications"
47  ." WHERE ClassificationId = ".intval($ParentId),
48  "NumberFound") < 1)
49  {
50  throw new InvalidArgumentException("Invalid parent ID"
51  ." specified (".$ParentId.").");
52  }
53  }
54 
55  # error out if name already exists
56  $Name = trim($Name);
57  $Count = $DB->Query("SELECT COUNT(*) AS NumberFound"
58  ." FROM Classifications"
59  ." WHERE ParentId = ".intval($ParentId)
60  ." AND FieldId = ".intval($FieldId)
61  ." AND LOWER(SegmentName) = '"
62  .addslashes(strtolower($Name))."'",
63  "NumberFound");
64  if ($Count > 0)
65  {
66  throw new Exception("Duplicate name specified for"
67  ." new classification (".$Name.").");
68  }
69 
70  # determine full name and depth for new classification
71  if ($ParentId == self::NOPARENT)
72  {
73  $NewName = $Name;
74  $NewDepth = 0;
75  }
76  else
77  {
78  $DB->Query("SELECT ClassificationName, Depth"
79  ." FROM Classifications"
80  ." WHERE ClassificationId = ".intval($ParentId));
81  $ParentInfo = $DB->FetchRow();
82  $NewName = $ParentInfo["ClassificationName"]." -- ".$Name;
83  $NewDepth = $ParentInfo["Depth"] + 1;
84  }
85 
86  # add classification to database
87  $InitialValues = array(
88  "FieldId" => $FieldId,
89  "ParentId" => $ParentId,
90  "SegmentName" => $Name,
91  "ResourceCount" => 0,
92  "Depth" => $NewDepth,
93  "ClassificationName" => $NewName);
94  $NewItem = parent::CreateWithValues($InitialValues);
95  }
96  else
97  {
98  # parse classification name into separate segments
99  $Segments = preg_split("/--/", $Name);
100 
101  # start out with top as parent
102  $ParentId = self::NOPARENT;
103 
104  # start off assuming we won't create anything
105  $NewItem = NULL;
106 
107  # for each segment
108  $CurrentDepth = -1;
109  $CurrentFullName = "";
110  foreach ($Segments as $Segment)
111  {
112  # track segment depth and full classification name for use
113  # in adding new entries
114  $Segment = trim($Segment);
115  $CurrentDepth++;
116  $CurrentFullName .= (($CurrentFullName == "") ? "" : " -- ").$Segment;
117 
118  # if we have added classifications
119  $Segment = addslashes($Segment);
120  if (self::$SegmentsCreated)
121  {
122  # we know that current segment will not be found
123  $ClassId = NULL;
124  }
125  else
126  {
127  # look up classification with current parent and segment name
128  if (!isset($IdCache[$FieldId][$ParentId][$Segment]))
129  {
130  if ($ParentId == self::NOPARENT)
131  {
132  $IdCache[$FieldId][$ParentId][$Segment] = $DB->Query(
133  "SELECT ClassificationId FROM Classifications"
134  ." WHERE ParentId = ".self::NOPARENT
135  ." AND SegmentName = '".addslashes($Segment)."'"
136  ." AND FieldId = ".intval($FieldId),
137  "ClassificationId");
138  }
139  else
140  {
141  $IdCache[$FieldId][$ParentId][$Segment] = $DB->Query(
142  "SELECT ClassificationId FROM Classifications "
143  ."WHERE ParentId = ".intval($ParentId)
144  ." AND SegmentName = '".addslashes($Segment)."'",
145  "ClassificationId");
146  }
147  }
148  $ClassId = $IdCache[$FieldId][$ParentId][$Segment];
149  }
150 
151  # if classification not found
152  if ($ClassId === NULL)
153  {
154  # add new classification
155  $InitialValues = array(
156  "FieldId" => $FieldId,
157  "ParentId" => $ParentId,
158  "SegmentName" => $Segment,
159  "ResourceCount" => 0,
160  "Depth" => $CurrentDepth,
161  "ClassificationName" => $CurrentFullName);
162  $NewItem = parent::CreateWithValues($InitialValues);
163  $ClassId = $NewItem->Id();
164  $IdCache[$FieldId][$ParentId][$Segment] = $ClassId;
165 
166  # track total number of new classification segments created
167  self::$SegmentsCreated++;
168  }
169 
170  # set parent to created or found class
171  $ParentId = $ClassId;
172  }
173 
174  # if it wasn't actually necessary to create anything
175  if ($NewItem === NULL)
176  {
177  throw new Exception(
178  "Duplicate name specified for"
179  ." new classification (".$Name.").");
180  }
181  }
182 
183  # return new classification to caller
184  return new self($NewItem->Id());
185  }
186 
191  public function Id()
192  {
193  return $this->Id;
194  }
195 
200  public function FullName()
201  {
202  return $this->ValueCache["ClassificationName"];
203  }
204 
211  public function Name($NewValue = DB_NOVALUE)
212  {
213  if ($NewValue !== DB_NOVALUE)
214  {
215  throw new InvalidArgumentException("Illegal argument supplied.");
216  }
217  return $this->FullName();
218  }
219 
224  public function VariantName()
225  {
226  return NULL;
227  }
228 
233  public function Depth()
234  {
235  return $this->ValueCache["Depth"];
236  }
237 
243  public function ResourceCount()
244  {
245  return $this->ValueCache["ResourceCount"];
246  }
247 
254  public function FullResourceCount()
255  {
256  return $this->ValueCache["FullResourceCount"];
257  }
258 
263  public static function SegmentsCreated()
264  {
265  return self::$SegmentsCreated;
266  }
267 
272  public function ParentId()
273  {
274  return $this->ValueCache["ParentId"];
275  }
276 
282  public function SegmentName($NewValue = DB_NOVALUE)
283  {
284  return $this->UpdateValue("SegmentName", $NewValue);
285  }
286 
294  public function LinkString($NewValue = DB_NOVALUE)
295  {
296  return $this->UpdateValue("LinkString", $NewValue);
297  }
298 
305  public function QualifierId($NewValue = DB_NOVALUE)
306  {
307  return $this->UpdateValue("QualifierId", $NewValue);
308  }
309 
315  public function FieldId($NewValue = DB_NOVALUE)
316  {
317  return $this->UpdateValue("FieldId", $NewValue);
318  }
319 
326  public function Qualifier($NewValue = DB_NOVALUE)
327  {
328  # if new qualifier supplied
329  if ($NewValue !== DB_NOVALUE)
330  {
331  # set new qualifier ID
332  $this->QualifierId($NewValue->Id());
333 
334  # use new qualifier for return value
335  $Qualifier = $NewValue;
336  }
337  else
338  {
339  # if qualifier is available
340  if ($this->QualifierId() !== NULL)
341  {
342  # create qualifier object using stored ID
343  $Qualifier = new Qualifier($this->QualifierId());
344  }
345  else
346  {
347  # return NULL to indicate no qualifier
348  $Qualifier = NULL;
349  }
350  }
351 
352  # return qualifier to caller
353  return $Qualifier;
354  }
355 
361  public function RecalcDepthAndFullName()
362  {
363  # start with full classification name set to our segment name
364  $FullClassName = $this->ValueCache["SegmentName"];
365 
366  # assume to begin with that we're at the top of the hierarchy
367  $Depth = 0;
368 
369  # while parent available
370  $ParentId = $this->ValueCache["ParentId"];
371  while ($ParentId != self::NOPARENT)
372  {
373  # retrieve classification information
374  $this->DB->Query("SELECT SegmentName, ParentId "
375  ."FROM Classifications "
376  ."WHERE ClassificationId=".$ParentId);
377  $Record = $this->DB->FetchRow();
378 
379  # prepend segment name to full classification name
380  $FullClassName = $Record["SegmentName"]." -- ".$FullClassName;
381 
382  # increment depth value
383  $Depth++;
384 
385  # move to parent of current classification
386  $ParentId = $Record["ParentId"];
387  }
388 
389  # for each child
390  $this->DB->Query("SELECT ClassificationId FROM Classifications"
391  ." WHERE ParentId = ".intval($this->Id));
392  while ($Record = $this->DB->FetchRow())
393  {
394  # perform depth and name recalc
395  $Child = new Classification($Record["ClassificationId"]);
396  $Child->RecalcDepthAndFullName();
397  }
398 
399  # save new depth and full classification name
400  $this->UpdateValue("Depth", $Depth);
401  $this->UpdateValue("ClassificationName", $FullClassName);
402  }
403 
407  public function UpdateLastAssigned()
408  {
409  $this->DB->Query("UPDATE Classifications SET LastAssigned=NOW() "
410  ."WHERE ClassificationId=".intval($this->Id));
411  }
412 
420  public function RecalcResourceCount($IdsToSkip = NULL)
421  {
422  $IdsUpdated = array();
423 
424  # if we don't have a skip list or we aren't in the skip list
425  if (!$IdsToSkip || !in_array($this->Id, $IdsToSkip))
426  {
427  # retrieve new count of resources directly associated with class
428  $this->DB->Query("SELECT R.ResourceId AS ResourceId, SchemaId"
429  ." FROM ResourceClassInts RCI, Resources R"
430  ." WHERE RCI.ClassificationId=".intval($this->Id)
431  ." AND RCI.ResourceId = R.ResourceId"
432  ." AND R.ResourceId > 0");
433 
434  # pull out resources and bin them by schema
435  $Resources = array();
436  while ($Row = $this->DB->FetchRow())
437  {
438  $Resources[$Row["SchemaId"]][]= $Row["ResourceId"];
439  }
440 
441  # filter out non-viewable resources from each schema
442  foreach ($Resources as $SchemaId => $ResourceIds)
443  {
444  $RFactory = new ResourceFactory($SchemaId);
445  $Resources[$SchemaId] = $RFactory->FilterNonViewableResources(
446  $ResourceIds, CWUser::GetAnonymousUser());
447  }
448 
449  # total up resources from each schema
450  $ResourceCount = 0;
451  foreach ($Resources as $SchemaId => $ResourceIds)
452  {
453  $ResourceCount += count($ResourceIds);
454  }
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
464  $this->UpdateValue("ResourceCount", $ResourceCount);
465 
466  # add our ID to list of IDs that have been recalculated
467  $IdsUpdated[] = $this->Id;
468  }
469 
470  # update resource count for our parent (if any)
471  if (($this->ValueCache["ParentId"] != self::NOPARENT)
472  && (!$IdsToSkip || !in_array($this->ValueCache["ParentId"], $IdsToSkip)) )
473  {
474  $Class = new Classification($this->ValueCache["ParentId"]);
475  $IdsUpdated = array_merge($IdsUpdated, $Class->RecalcResourceCount());
476  }
477 
478  # retrieve new count of all resources directly associated with class
479  $FullCount = $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
480  ." FROM ResourceClassInts I, Resources R"
481  ." WHERE I.ClassificationId = ".intval($this->Id)
482  ." AND R.ResourceId > 0"
483  ." AND I.ResourceId = R.ResourceId",
484  "ResourceCount");
485 
486  # add on resources associated with all children
487  $FullCount += $this->DB->Query(
488  "SELECT SUM(ResourceCount) AS ResourceCountTotal"
489  ." FROM Classifications"
490  ." WHERE ParentId = ".intval($this->Id),
491  "ResourceCountTotal");
492 
493  # save new full count
494  $this->UpdateValue("FullResourceCount", $ResourceCount);
495 
496  # return list of IDs of updated classifications to caller
497  return $IdsUpdated;
498  }
499 
504  public function ChildCount()
505  {
506  # return count of classifications that have this one as parent
507  return $this->DB->Query("SELECT COUNT(*) AS ClassCount "
508  ."FROM Classifications "
509  ."WHERE ParentId=".intval($this->Id),
510  "ClassCount");
511  }
512 
518  public function ChildList()
519  {
520  $ChildList = array();
521 
522  $this->DB->Query("SELECT ClassificationId "
523  ."FROM Classifications "
524  ."WHERE ParentId=".intval($this->Id));
525 
526  while ($Entry = $this->DB->FetchRow())
527  {
528  $ChildList[] = $Entry["ClassificationId"];
529  $Child = new Classification($Entry["ClassificationId"]);
530  if($Child->ChildCount() > 0)
531  {
532  $GrandChildList = $Child->ChildList();
533  $ChildList = array_merge($GrandChildList, $ChildList);
534  }
535  }
536  return array_unique($ChildList);
537  }
538 
552  public function Delete($DeleteParents = FALSE,
553  $DeleteIfHasResources = FALSE, $DeleteIfHasChildren = FALSE)
554  {
555  # if no resources or okay to delete with resources
556  # and no children or okay to delete with children
557  $DeleteCount = 0;
558  if (($DeleteIfHasResources || ($this->ResourceCount() == 0))
559  && ($DeleteIfHasChildren || ($this->ChildCount() == 0)))
560  {
561  if ($this->ResourceCount() != 0)
562  {
563  $this->DB->Query("DELETE FROM ResourceClassInts"
564  ." WHERE ClassificationId = ".intval($this->Id));
565  $this->RecalcResourceCount();
566  }
567 
568  # delete this classification
569  parent::Delete();
570  $DeleteCount++;
571 
572  # delete parent classification (if requested)
573  $ParentId = $this->ValueCache["ParentId"];
574  if (($DeleteParents) && ($ParentId != self::NOPARENT))
575  {
576  $Parent = new Classification($ParentId);
577  $DeleteCount += $Parent->Delete(
578  TRUE, $DeleteIfHasResources, $DeleteIfHasChildren);
579  }
580  }
581 
582  # return total number of classifications deleted to caller
583  return $DeleteCount;
584  }
585 
586 
587  # ---- PRIVATE INTERFACE -------------------------------------------------
588 
589  static private $SegmentsCreated;
590 }
LinkString($NewValue=DB_NOVALUE)
Get or set the stored link string for the Classification.
static GetAnonymousUser()
Get the anonymous user (i.e., the User object that exists when no user is logged in), useful when a permission check needs to know if something should be visible to the general public.
Definition: User.php:1034
static SegmentsCreated()
Get number of new segments (Classifications) generated when creating a new Classification with a full...
FullName()
Get full classification name (all segments).
FullResourceCount()
Get number of all resources (minus temporary ones) having this classification assigned to them...
UpdateValue($ColumnName, $NewValue=DB_NOVALUE)
Convenience function to supply parameters to Database::UpdateValue().
Definition: Item.php:255
SQL database abstraction object with smart query caching.
Definition: Database.php:22
static Create($Name, $FieldId, $ParentId=NULL)
Add new classification to the hierarchy.
FieldId($NewValue=DB_NOVALUE)
Get or set the ID of the MetadataField for the Classification.
VariantName()
Get variant name of classification, if any.
$DB
Definition: Item.php:181
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.
const NOPARENT
Parent value for classifications with no parent.
ParentId()
Get ID of parent Classification.
RecalcDepthAndFullName()
Rebuild classification full name and recalculate depth in hierarchy.
Common base class for persistent items store in database.
Definition: Item.php:13
const DB_NOVALUE
Definition: Database.php:1675
Depth()
Get depth of classification in hierarchy.
Name($NewValue=DB_NOVALUE)
Get full classification name (all segments).
ResourceCount()
Get number of released resources having this classification assigned to them.
$Id
Definition: Item.php:182
Delete($DeleteParents=FALSE, $DeleteIfHasResources=FALSE, $DeleteIfHasChildren=FALSE)
Remove Classification (and accompanying associations) from database.
UpdateLastAssigned()
Update the LastAssigned timestamp for this classification.
Metadata type representing hierarchical ("Tree") controlled vocabulary values.
Factory for Resource objects.
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.