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