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  && Qualifier::ItemExists($this->QualifierId()))
342  {
343  # create qualifier object using stored ID
344  $Qualifier = new Qualifier($this->QualifierId());
345  }
346  else
347  {
348  # return NULL to indicate no qualifier
349  $Qualifier = NULL;
350  }
351  }
352 
353  # return qualifier to caller
354  return $Qualifier;
355  }
356 
362  public function RecalcDepthAndFullName()
363  {
364  # start with full classification name set to our segment name
365  $FullClassName = $this->ValueCache["SegmentName"];
366 
367  # assume to begin with that we're at the top of the hierarchy
368  $Depth = 0;
369 
370  # while parent available
371  $ParentId = $this->ValueCache["ParentId"];
372  while ($ParentId != self::NOPARENT)
373  {
374  # retrieve classification information
375  $this->DB->Query("SELECT SegmentName, ParentId "
376  ."FROM Classifications "
377  ."WHERE ClassificationId=".$ParentId);
378  $Record = $this->DB->FetchRow();
379 
380  # prepend segment name to full classification name
381  $FullClassName = $Record["SegmentName"]." -- ".$FullClassName;
382 
383  # increment depth value
384  $Depth++;
385 
386  # move to parent of current classification
387  $ParentId = $Record["ParentId"];
388  }
389 
390  # for each child
391  $this->DB->Query("SELECT ClassificationId FROM Classifications"
392  ." WHERE ParentId = ".intval($this->Id));
393  while ($Record = $this->DB->FetchRow())
394  {
395  # perform depth and name recalc
396  $Child = new Classification($Record["ClassificationId"]);
397  $Child->RecalcDepthAndFullName();
398  }
399 
400  # save new depth and full classification name
401  $this->UpdateValue("Depth", $Depth);
402  $this->UpdateValue("ClassificationName", $FullClassName);
403  }
404 
408  public function UpdateLastAssigned()
409  {
410  $this->DB->Query("UPDATE Classifications SET LastAssigned=NOW() "
411  ."WHERE ClassificationId=".intval($this->Id));
412  }
413 
421  public function RecalcResourceCount($IdsToSkip = NULL)
422  {
423  $IdsUpdated = array();
424 
425  # if we don't have a skip list or we aren't in the skip list
426  if (!$IdsToSkip || !in_array($this->Id, $IdsToSkip))
427  {
428  # retrieve new count of resources directly associated with class
429  $this->DB->Query("SELECT R.ResourceId AS ResourceId, SchemaId"
430  ." FROM ResourceClassInts RCI, Resources R"
431  ." WHERE RCI.ClassificationId=".intval($this->Id)
432  ." AND RCI.ResourceId = R.ResourceId"
433  ." AND R.ResourceId > 0");
434 
435  # pull out resources and bin them by schema
436  $Resources = array();
437  while ($Row = $this->DB->FetchRow())
438  {
439  $Resources[$Row["SchemaId"]][]= $Row["ResourceId"];
440  }
441 
442  # filter out non-viewable resources from each schema
443  foreach ($Resources as $SchemaId => $ResourceIds)
444  {
445  $RFactory = new ResourceFactory($SchemaId);
446  $Resources[$SchemaId] = $RFactory->FilterNonViewableResources(
447  $ResourceIds, CWUser::GetAnonymousUser());
448  }
449 
450  # total up resources from each schema
451  $ResourceCount = 0;
452  foreach ($Resources as $SchemaId => $ResourceIds)
453  {
454  $ResourceCount += count($ResourceIds);
455  }
456 
457  # add on resources associated with all children
458  $ResourceCount += $this->DB->Query(
459  "SELECT SUM(ResourceCount) AS ResourceCountTotal "
460  ."FROM Classifications "
461  ."WHERE ParentId = ".intval($this->Id),
462  "ResourceCountTotal");
463 
464  # save new count
465  $this->UpdateValue("ResourceCount", $ResourceCount);
466 
467  # add our ID to list of IDs that have been recalculated
468  $IdsUpdated[] = $this->Id;
469  }
470 
471  # update resource count for our parent (if any)
472  if (($this->ValueCache["ParentId"] != self::NOPARENT)
473  && (!$IdsToSkip || !in_array($this->ValueCache["ParentId"], $IdsToSkip)) )
474  {
475  $Class = new Classification($this->ValueCache["ParentId"]);
476  $IdsUpdated = array_merge($IdsUpdated, $Class->RecalcResourceCount());
477  }
478 
479  # retrieve new count of all resources directly associated with class
480  $FullCount = $this->DB->Query("SELECT COUNT(*) AS ResourceCount"
481  ." FROM ResourceClassInts I, Resources R"
482  ." WHERE I.ClassificationId = ".intval($this->Id)
483  ." AND R.ResourceId > 0"
484  ." AND I.ResourceId = R.ResourceId",
485  "ResourceCount");
486 
487  # add on resources associated with all children
488  $FullCount += $this->DB->Query(
489  "SELECT SUM(ResourceCount) AS ResourceCountTotal"
490  ." FROM Classifications"
491  ." WHERE ParentId = ".intval($this->Id),
492  "ResourceCountTotal");
493 
494  # save new full count
495  $this->UpdateValue("FullResourceCount", $ResourceCount);
496 
497  # return list of IDs of updated classifications to caller
498  return $IdsUpdated;
499  }
500 
505  public function ChildCount()
506  {
507  # return count of classifications that have this one as parent
508  return $this->DB->Query("SELECT COUNT(*) AS ClassCount "
509  ."FROM Classifications "
510  ."WHERE ParentId=".intval($this->Id),
511  "ClassCount");
512  }
513 
519  public function ChildList()
520  {
521  $ChildList = array();
522 
523  $this->DB->Query("SELECT ClassificationId "
524  ."FROM Classifications "
525  ."WHERE ParentId=".intval($this->Id));
526 
527  while ($Entry = $this->DB->FetchRow())
528  {
529  $ChildList[] = $Entry["ClassificationId"];
530  $Child = new Classification($Entry["ClassificationId"]);
531  if($Child->ChildCount() > 0)
532  {
533  $GrandChildList = $Child->ChildList();
534  $ChildList = array_merge($GrandChildList, $ChildList);
535  }
536  }
537  return array_unique($ChildList);
538  }
539 
553  public function Delete($DeleteParents = FALSE,
554  $DeleteIfHasResources = FALSE, $DeleteIfHasChildren = FALSE)
555  {
556  # if no resources or okay to delete with resources
557  # and no children or okay to delete with children
558  $DeleteCount = 0;
559  if (($DeleteIfHasResources || ($this->ResourceCount() == 0))
560  && ($DeleteIfHasChildren || ($this->ChildCount() == 0)))
561  {
562  if ($this->ResourceCount() != 0)
563  {
564  $this->DB->Query("DELETE FROM ResourceClassInts"
565  ." WHERE ClassificationId = ".intval($this->Id));
566  $this->RecalcResourceCount();
567  }
568 
569  # delete this classification
570  parent::Delete();
571  $DeleteCount++;
572 
573  # delete parent classification (if requested)
574  $ParentId = $this->ValueCache["ParentId"];
575  if (($DeleteParents) && ($ParentId != self::NOPARENT))
576  {
577  $Parent = new Classification($ParentId);
578  $DeleteCount += $Parent->Delete(
579  TRUE, $DeleteIfHasResources, $DeleteIfHasChildren);
580  }
581  }
582 
583  # return total number of classifications deleted to caller
584  return $DeleteCount;
585  }
586 
587 
588  # ---- PRIVATE INTERFACE -------------------------------------------------
589 
590  static private $SegmentsCreated;
591 }
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:1039
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:284
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:210
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:1738
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:211
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.
static ItemExists($Id)
Check whether an item exists with the specified ID.
Definition: Item.php:162
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.