Continued:
[core.git] / inc / main / classes / database / result / class_CachedDatabaseResult.php
1 <?php
2 // Own namespace
3 namespace CoreFramework\Database\Result;
4
5 /**
6  * A database result class
7  *
8  * @author              Roland Haeder <webmaster@shipsimu.org>
9  * @version             0.0.0
10  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Core Developer Team
11  * @license             GNU GPL 3.0 or any newer version
12  * @link                http://www.shipsimu.org
13  *
14  * This program is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation, either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program. If not, see <http://www.gnu.org/licenses/>.
26  */
27 class CachedDatabaseResult extends BaseDatabaseResult implements SearchableResult, UpdateableResult, SeekableIterator {
28         // Exception constants
29         const EXCEPTION_INVALID_DATABASE_RESULT = 0x1c0;
30         const EXCEPTION_RESULT_UPDATE_FAILED    = 0x1c1;
31
32         /**
33          * Current position in array
34          */
35         private $currentPos = -1;
36
37         /**
38          * Current row
39          */
40         private $currentRow = NULL;
41
42         /**
43          * Result array
44          */
45         private $resultArray = array();
46
47         /**
48          * Array of out-dated entries
49          */
50         private $outDated = array();
51
52         /**
53          * Affected rows
54          */
55         private $affectedRows = 0;
56
57         /**
58          * Found value
59          */
60         private $foundValue = '';
61
62         /**
63          * Protected constructor
64          *
65          * @return      void
66          */
67         protected function __construct () {
68                 // Call parent constructor
69                 parent::__construct(__CLASS__);
70         }
71
72         /**
73          * Creates an instance of this result by a provided result array
74          *
75          * @param       $resultArray            The array holding the result from query
76          * @return      $resultInstance         An instance of this class
77          */
78         public static final function createCachedDatabaseResult (array $resultArray) {
79                 // Get a new instance
80                 $resultInstance = new CachedDatabaseResult();
81
82                 // Set the result array
83                 $resultInstance->setResultArray($resultArray);
84
85                 // Set affected rows
86                 $resultInstance->setAffectedRows(count($resultArray[BaseDatabaseBackend::RESULT_INDEX_ROWS]));
87
88                 // Return the instance
89                 return $resultInstance;
90         }
91
92         /**
93          * Setter for result array
94          *
95          * @param       $resultArray    The array holding the result from query
96          * @return      void
97          */
98         protected final function setResultArray (array $resultArray) {
99                 $this->resultArray = $resultArray;
100         }
101
102         /**
103          * Updates the current entry by given update criteria
104          *
105          * @param       $updateInstance         An instance of an Updateable criteria
106          * @return      void
107          */
108         private function updateCurrentEntryByCriteria (LocalUpdateCriteria $updateInstance) {
109                 // Get the current entry key
110                 $entryKey = $this->key();
111
112                 // Now get the update criteria array and update all entries
113                 foreach ($updateInstance->getUpdateCriteria() as $criteriaKey => $criteriaValue) {
114                         // Update data
115                         $this->resultArray[BaseDatabaseBackend::RESULT_INDEX_ROWS][$entryKey][$criteriaKey] = $criteriaValue;
116
117                         // Mark it as out-dated
118                         $this->outDated[$criteriaKey] = 1;
119                 } // END - foreach
120         }
121
122         /**
123          * "Iterator" method next() to advance to the next valid entry. This method
124          * does also check if result is invalid
125          *
126          * @return      $nextValid      Whether the next entry is valid
127          */
128         public function next () {
129                 // Default is not valid
130                 $nextValid = FALSE;
131
132                 // Is the result valid?
133                 if ($this->valid()) {
134                         // Next entry found, so count one up and cache it
135                         $this->currentPos++;
136                         $this->currentRow = $this->resultArray[BaseDatabaseBackend::RESULT_INDEX_ROWS][$this->currentPos];
137                         $nextValid = TRUE;
138                 } // END - if
139
140                 // Return the result
141                 return $nextValid;
142         }
143
144         /**
145          * Seeks for to a specified position
146          *
147          * @param       $index  Index to seek for
148          * @return      void
149          */
150         public function seek ($index) {
151                 // Rewind to beginning
152                 $this->rewind();
153
154                 // Search for the entry
155                 while (($this->currentPos < $index) && ($this->valid())) {
156                         // Continue on
157                         $this->next();
158                 } // END - while
159         }
160
161         /**
162          * Gives back the current position or null if not found
163          *
164          * @return      $current        Current element to give back
165          */
166         public function current () {
167                 // Default is not found
168                 $current = NULL;
169
170                 // Does the current enty exist?
171                 if (isset($this->resultArray[BaseDatabaseBackend::RESULT_INDEX_ROWS][$this->currentPos])) {
172                         // Then get it
173                         $current = $this->resultArray[BaseDatabaseBackend::RESULT_INDEX_ROWS][$this->currentPos];
174                 } // END - if
175
176                 // Return the result
177                 return $current;
178         }
179
180         /**
181          * Checks if next() and rewind will give a valid result
182          *
183          * @return      $isValid Whether the next/rewind entry is valid
184          */
185         public function valid () {
186                 // By default nothing is valid
187                 $isValid = FALSE;
188
189                 // Debug message
190                 //*NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . '] this->currentPos=' . $this->currentPos);
191
192                 // Check if all is fine ...
193                 if (($this->ifStatusIsOkay()) && (isset($this->resultArray[BaseDatabaseBackend::RESULT_INDEX_ROWS][($this->currentPos + 1)])) && (isset($this->resultArray[BaseDatabaseBackend::RESULT_INDEX_ROWS][0]))) {
194                         // All fine!
195                         $isValid = TRUE;
196                 } // END - if
197
198                 // Return the result
199                 return $isValid;
200         }
201
202         /**
203          * Returns count of entries
204          *
205          * @return      $isValid Whether the next/rewind entry is valid
206          */
207         public function count () {
208                 // Return it
209                 return count($this->resultArray[BaseDatabaseBackend::RESULT_INDEX_ROWS]);
210         }
211
212         /**
213          * Determines whether the status of the query was fine (BaseDatabaseBackend::RESULT_OKAY)
214          *
215          * @return      $ifStatusOkay   Whether the status of the query was okay
216          */
217         public function ifStatusIsOkay () {
218                 $ifStatusOkay = ((isset($this->resultArray[BaseDatabaseBackend::RESULT_INDEX_STATUS])) && ($this->resultArray[BaseDatabaseBackend::RESULT_INDEX_STATUS] === BaseDatabaseBackend::RESULT_OKAY));
219                 //*NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . '] ifStatusOkay=' . intval($ifStatusOkay));
220                 return $ifStatusOkay;
221         }
222
223         /**
224          * Gets the current key of iteration
225          *
226          * @return      $currentPos     Key from iterator
227          */
228         public function key () {
229                 return $this->currentPos;
230         }
231
232         /**
233          * Rewind to the beginning and clear array $currentRow
234          *
235          * @return      void
236          */
237         public function rewind () {
238                 $this->currentPos = -1;
239                 $this->currentRow = array();
240         }
241
242         /**
243          * Searches for an entry in data result and returns it
244          *
245          * @param       $criteriaInstance       The criteria to look inside the data set
246          * @return      $result                         Found result entry
247          * @todo        0% done
248          */
249         public function searchEntry (LocalSearchCriteria $criteriaInstance) {
250                 $this->debugBackTrace('[' . '[' . __METHOD__ . ':' . __LINE__ . ']:  Unfinished!');
251         }
252
253         /**
254          * Adds an update request to the database result for writing it to the
255          * database layer
256          *
257          * @param       $criteriaInstance       An instance of a updateable criteria
258          * @return      void
259          * @throws      ResultUpdateException   If no result was updated
260          */
261         public function add2UpdateQueue (LocalUpdateCriteria $criteriaInstance) {
262                 // Rewind the pointer
263                 $this->rewind();
264
265                 // Get search criteria
266                 $searchInstance = $criteriaInstance->getSearchInstance();
267
268                 // And start looking for the result
269                 $foundEntries = 0;
270                 while (($this->valid()) && ($foundEntries < $searchInstance->getLimit())) {
271                         // Get next entry
272                         $this->next();
273                         $currentEntry = $this->current();
274
275                         // Is this entry found?
276                         if ($searchInstance->ifEntryMatches($currentEntry)) {
277                                 // Update this entry
278                                 $this->updateCurrentEntryByCriteria($criteriaInstance);
279
280                                 // Count one up
281                                 $foundEntries++;
282                         } // END - if
283                 } // END - while
284
285                 // If no entry is found/updated throw an exception
286                 if ($foundEntries == 0) {
287                         // Throw an exception here
288                         throw new ResultUpdateException($this, self::EXCEPTION_RESULT_UPDATE_FAILED);
289                 } // END - if
290
291                 // Set affected rows
292                 $this->setAffectedRows($foundEntries);
293
294                 // Set update instance
295                 $this->setUpdateInstance($criteriaInstance);
296         }
297
298         /**
299          * Setter for affected rows
300          *
301          * @param       $rows   Number of affected rows
302          * @return      void
303          */
304         public final function setAffectedRows ($rows) {
305                 $this->affectedRows = $rows;
306         }
307
308         /**
309          * Getter for affected rows
310          *
311          * @return      $rows   Number of affected rows
312          */
313         public final function getAffectedRows () {
314                 return $this->affectedRows;
315         }
316
317         /**
318          * Getter for found value of previous found() call
319          *
320          * @return      $foundValue             Found value of previous found() call
321          */
322         public final function getFoundValue () {
323                 return $this->foundValue;
324         }
325
326         /**
327          * Checks whether we have out-dated entries or not
328          *
329          * @return      $needsUpdate    Whether we have out-dated entries
330          */
331         public function ifDataNeedsFlush () {
332                 $needsUpdate = (count($this->outDated) > 0);
333                 return $needsUpdate;
334         }
335
336         /**
337          * Adds registration elements to a given dataset instance
338          *
339          * @param       $criteriaInstance       An instance of a StoreableCriteria class
340          * @param       $requestInstance        An instance of a Requestable class
341          * @return      void
342          */
343         public function addElementsToDataSet (StoreableCriteria $criteriaInstance, Requestable $requestInstance = NULL) {
344                 // Walk only through out-dated columns
345                 foreach ($this->outDated as $key => $dummy) {
346                         // Does this key exist?
347                         //* DEBUG: */ echo "outDated: {$key}<br />\n";
348                         if ($this->find($key)) {
349                                 // Then update it
350                                 $criteriaInstance->addCriteria($key, $this->getFoundValue());
351                         } // END - if
352                 } // END - foreach
353         }
354
355         /**
356          * Find a key inside the result array
357          *
358          * @param       $key    The key we shall find
359          * @return      $found  Whether the key was found or not
360          */
361         public function find ($key) {
362                 // By default nothing is found
363                 $found = FALSE;
364
365                 // Rewind the pointer
366                 $this->rewind();
367
368                 // Walk through all entries
369                 while ($this->valid()) {
370                         // Advance to next entry
371                         $this->next();
372
373                         // Get the whole array
374                         $currentEntry = $this->current();
375
376                         // Is the element there?
377                         if (isset($currentEntry[$key])) {
378                                 // Okay, found!
379                                 $found = TRUE;
380
381                                 // So "cache" it
382                                 $this->foundValue = $currentEntry[$key];
383
384                                 // And stop searching
385                                 break;
386                         } // END - if
387                 } // END - while
388
389                 // Return the result
390                 return $found;
391         }
392
393         /**
394          * Solver for result index value with call-back method
395          *
396          * @param       $databaseColumn         Database column where the index might be found
397          * @param       $wrapperInstance        The wrapper instance to ask for array element
398          * @para        $callBack                       Call-back object for setting the index;
399          *                                                              0=object instance,1=method name
400          * @return      void
401          * @todo        Find a caching way without modifying the result array
402          */
403         public function solveResultIndex ($databaseColumn, DatabaseWrapper $wrapperInstance, array $callBack) {
404                 // By default nothing is found
405                 $indexValue = 0;
406
407                 // Is the element in result itself found?
408                 if ($this->find($databaseColumn)) {
409                         // Use this value
410                         $indexValue = $this->getFoundValue();
411                 } elseif ($this->find($wrapperInstance->getIndexKey())) {
412                         // Use this value
413                         $indexValue = $this->getFoundValue();
414                 }
415
416                 // Set the index
417                 call_user_func_array($callBack, array($indexValue));
418         }
419
420 }