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