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