CWIS Developer Documentation
DoublyLinkedItemList.php
Go to the documentation of this file.
1 <?PHP
2 
3 #
4 # FILE: SPT--DoublyLinkedList.php
5 #
6 # METHODS PROVIDED:
7 # DoublyLinkedList()
8 # - constructor
9 # SomeMethod($SomeParameter, $AnotherParameter)
10 # - short description of method
11 #
12 # AUTHOR:
13 #
14 # Part of the Scout Portal Toolkit
15 # Copyright 2007 Internet Scout
16 # http://scout.wisc.edu
17 #
18 
20 
21  # ---- PUBLIC INTERFACE --------------------------------------------------
22 
23  # object constructor
25  $ItemTableName, $ItemIdFieldName, $SqlCondition = NULL)
26  {
27  # grab our own database handle
28  $this->DB = new Database();
29 
30  # save configuration
31  $this->ItemIdFieldName = $ItemIdFieldName;
32  $this->ItemTableName = $ItemTableName;
33  $this->SqlCondition($SqlCondition);
34  }
35 
36  # insert/move item to before specified item
37  function InsertBefore($SourceItemOrItemId, $TargetItemOrItemId)
38  {
39  # retrieve item IDs
40  $SourceItemId = is_object($SourceItemOrItemId)
41  ? $SourceItemOrItemId->Id() : $SourceItemOrItemId;
42  $TargetItemId = is_object($TargetItemOrItemId)
43  ? $TargetItemOrItemId->Id() : $TargetItemOrItemId;
44 
45  # remove source item from current position if necessary
46  $this->Remove($SourceItemId);
47 
48  # update IDs to link in new item
49  $CurrentTargetItemPreviousId = $this->GetPreviousItemId($TargetItemId);
50  $this->SetPreviousItemId($TargetItemId, $SourceItemId);
51  if ($CurrentTargetItemPreviousId != -1)
52  {
53  $this->SetNextItemId($CurrentTargetItemPreviousId, $SourceItemId);
54  }
55  $this->SetPreviousAndNextItemIds($SourceItemId,
56  $CurrentTargetItemPreviousId, $TargetItemId);
57  }
58 
59  # insert/move item to after specified item
60  function InsertAfter($SourceItemOrItemId, $TargetItemOrItemId)
61  {
62  # retrieve item IDs
63  $SourceItemId = is_object($SourceItemOrItemId)
64  ? $SourceItemOrItemId->Id() : $SourceItemOrItemId;
65  $TargetItemId = is_object($TargetItemOrTargetItemId)
66  ? $TargetItemOrTargetItemId->Id() : $TargetItemOrTargetItemId;
67 
68  # remove source item from current position if necessary
69  $this->Remove($SourceItemId);
70 
71  # update IDs to link in new item
72  $CurrentTargetItemNextId = $this->GetNextItemIdInOrder($TargetItemId);
73  $this->SetNextItemId($TargetItemId, $SourceItemId);
74  if ($CurrentTargetItemNextId != -1)
75  {
76  $this->SetPreviousItemId($CurrentTargetItemNextId, $SourceItemId);
77  }
78  $this->SetPreviousAndNextItemIds($SourceItemId,
79  $TargetItemId, $CurrentTargetItemNextId);
80  }
81 
82  # add/move item to beginning of list
83  function Prepend($ItemOrItemId)
84  {
85  # get item ID
86  $ItemId = is_object($ItemOrItemId) ? $ItemOrItemId->Id() : $ItemOrItemId;
87 
88  # remove new item from current position if necessary
89  $this->Remove($ItemId);
90 
91  # if there are items currently in list
92  $ItemIds = $this->GetIds(FALSE);
93  if (count($ItemIds))
94  {
95  # link last item to source item
96  $FirstItemId = array_shift($ItemIds);
97  $this->SetPreviousItemId($FirstItemId, $ItemId);
98  $this->SetPreviousAndNextItemIds($ItemId, -1, $FirstItemId);
99  }
100  else
101  {
102  # add item to list as only item
103  $this->SetPreviousAndNextItemIds($ItemId, -1, -1);
104  }
105  }
106 
107  # add/move item to end of list
108  function Append($ItemOrItemId)
109  {
110  # get item ID
111  $ItemId = is_object($ItemOrItemId) ? $ItemOrItemId->Id() : $ItemOrItemId;
112 
113  # remove item from current position if necessary
114  $this->Remove($ItemId);
115 
116  # if there are items currently in list
117  $ItemIds = $this->GetIds(FALSE);
118  if (count($ItemIds))
119  {
120  # link last item to source item
121  $LastItemId = array_pop($ItemIds);
122  $this->SetNextItemId($LastItemId, $ItemId);
123  $this->SetPreviousAndNextItemIds($ItemId, $LastItemId, -1);
124  }
125  else
126  {
127  # add item to list as only item
128  $this->SetPreviousAndNextItemIds($ItemId, -1, -1);
129  }
130  }
131 
132  # retrieve list of item IDs in order
133  function GetIds($AddStrayItemsToOrder = TRUE)
134  {
135  # retrieve ordering IDs
136  $this->DB->Query("SELECT ".$this->ItemIdFieldName
137  .", Previous".$this->ItemIdFieldName
138  .", Next".$this->ItemIdFieldName
139  ." FROM ".$this->ItemTableName
140  .$this->GetCondition(TRUE)
141  ." ORDER BY ".$this->ItemIdFieldName." ASC");
142  $PreviousItemIds = array();
143  $NextItemIds = array();
144  while ($Record = $this->DB->FetchRow())
145  {
146  $ItemId = intval($Record[$this->ItemIdFieldName]);
147  $PreviousItemIds[$ItemId] =
148  intval($Record["Previous".$this->ItemIdFieldName]);
149  $NextItemIds[$ItemId] = intval($Record["Next".$this->ItemIdFieldName]);
150  }
151 
152  # pull unordered items out of list
153  $StrayItemIds = array_keys($PreviousItemIds, -2);
154  foreach ($StrayItemIds as $StrayItemId)
155  {
156  unset($PreviousItemIds[$StrayItemId]);
157  unset($NextItemIds[$StrayItemId]);
158  }
159 
160  # find first item
161  $ItemId = array_search(-1, $PreviousItemIds);
162 
163  # if first item was found
164  $ItemIds = array();
165  if ($ItemId !== FALSE)
166  {
167  # traverse linked list to build list of item IDs
168  do
169  {
170  $ItemIds[] = $ItemId;
171  unset($PreviousItemIds[$ItemId]);
172  if (isset($NextItemIds[$ItemId])) { $ItemId = $NextItemIds[$ItemId]; }
173  }
174  while (isset($NextItemIds[$ItemId]) && ($ItemId != -1)
175  && !in_array($ItemId, $ItemIds));
176 
177  # add any items left over to stray items list
178  $StrayItemIds = array_unique($StrayItemIds + array_keys($PreviousItemIds));
179  }
180 
181  # add any stray items to end of list (if so configured)
182  if ($AddStrayItemsToOrder)
183  {
184  foreach ($StrayItemIds as $StrayItemId)
185  {
186  $this->Append($StrayItemId);
187  $ItemIds[] = $StrayItemId;
188  }
189  }
190 
191  # return list of item IDs to caller
192  return $ItemIds;
193  }
194 
195  # remove item from existing order
196  function Remove($ItemId)
197  {
198  $CurrentItemPreviousId = $this->GetPreviousItemId($ItemId);
199  $CurrentItemNextId = $this->GetNextItemIdInOrder($ItemId);
200  if ($CurrentItemPreviousId >= 0)
201  {
202  $this->SetNextItemId(
203  $CurrentItemPreviousId, $CurrentItemNextId);
204  }
205  if ($CurrentItemNextId >= 0)
206  {
207  $this->SetPreviousItemId(
208  $CurrentItemNextId, $CurrentItemPreviousId);
209  }
210  }
211 
212  # set SQL condition for ordering operations
213  # (use NULL to clear condition)
214  function SqlCondition($NewCondition)
215  {
216  $this->Condition = $NewCondition;
217  }
218 
219 
220  # ---- PRIVATE INTERFACE -------------------------------------------------
221 
222  var $DB;
226 
227  # get/set ordering values
228  function GetPreviousItemId($ItemId)
229  {
230  return $this->DB->Query("SELECT Previous".$this->ItemIdFieldName
231  ." FROM ".$this->ItemTableName
232  ." WHERE ".$this->ItemIdFieldName." = ".intval($ItemId)
233  .$this->GetCondition(),
234  "Previous".$this->ItemIdFieldName);
235  }
236  function GetNextItemIdInOrder($ItemId)
237  {
238  return $this->DB->Query("SELECT Next".$this->ItemIdFieldName
239  ." FROM ".$this->ItemTableName
240  ." WHERE ".$this->ItemIdFieldName." = ".intval($ItemId)
241  .$this->GetCondition(),
242  "Next".$this->ItemIdFieldName);
243  }
244  function SetPreviousItemId($ItemId, $NewValue)
245  {
246  $this->DB->Query("UPDATE ".$this->ItemTableName
247  ." SET Previous".$this->ItemIdFieldName." = ".intval($NewValue)
248  ." WHERE ".$this->ItemIdFieldName." = ".intval($ItemId)
249  .$this->GetCondition());
250  }
251  function SetNextItemId($ItemId, $NewValue)
252  {
253  $this->DB->Query("UPDATE ".$this->ItemTableName
254  ." SET Next".$this->ItemIdFieldName." = ".intval($NewValue)
255  ." WHERE ".$this->ItemIdFieldName." = ".intval($ItemId)
256  .$this->GetCondition());
257  }
258  function SetPreviousAndNextItemIds($ItemId, $NewPreviousId, $NewNextId)
259  {
260  $this->DB->Query("UPDATE ".$this->ItemTableName
261  ." SET Previous".$this->ItemIdFieldName." = ".intval($NewPreviousId)
262  .", Next".$this->ItemIdFieldName." = ".intval($NewNextId)
263  ." WHERE ".$this->ItemIdFieldName." = ".intval($ItemId)
264  .$this->GetCondition());
265  }
266 
267  # return DB query condition (if any) with proper additional syntax
268  function GetCondition($ThisIsOnlyCondition = FALSE)
269  {
270  return $this->Condition ?
271  ($ThisIsOnlyCondition ? " WHERE " : " AND ").$this->Condition
272  : "";
273  }
274 }
275 
276 
277 ?>