![]() |
Xataface 2.0
Xataface Application Framework
|
00001 <?php 00002 /*------------------------------------------------------------------------------- 00003 * Xataface Web Application Framework 00004 * Copyright (C) 2005-2008 Web Lite Solutions Corp (shannah@sfu.ca) 00005 * 00006 * This program is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU General Public License 00008 * as published by the Free Software Foundation; either version 2 00009 * of the License, or (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software 00018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00019 *------------------------------------------------------------------------------- 00020 */ 00021 /******************************************************************************* 00022 * File: Dataface/QueryTool.php 00023 * Author: Steve Hannah <shannah@sfu.ca> 00024 * Created: September 4, 2005 00025 * Description: 00026 * Encapsulates query results from a table. 00027 ******************************************************************************/ 00028 import( 'Dataface/QueryBuilder.php'); 00029 import( 'Dataface/Table.php'); 00030 import( 'Dataface/Record.php'); 00031 import( 'Dataface/DB.php'); 00032 00033 00034 $GLOBALS['Dataface_QueryTool_limit'] = 30; 00035 $GLOBALS['Dataface_QueryTool_skip'] = 0; 00036 class Dataface_QueryTool { 00037 00038 var $_table; 00039 var $_db; 00040 var $_tablename; 00041 00042 var $_query; 00043 00044 var $_data; 00045 00046 var $_currentRecord = null; 00047 var $_titles; 00048 00049 var $dbObj = null; 00050 00051 function &staticCache(){ 00052 static $cache = 0; 00053 if ( $cache === 0 ){ 00054 $cache = array(); 00055 } 00056 return $cache; 00057 } 00058 00059 00066 function Dataface_QueryTool($tablename, $db=null, $query=null){ 00067 $this->dbObj =& Dataface_DB::getInstance(); 00068 $this->_tablename = $tablename; 00069 if ( !is_array($query) ) $query= array(); 00070 if ( $db === null ){ 00071 $db = DATAFACE_DB_HANDLE; 00072 } 00073 $this->_db = $db; 00074 $this->_query = $query; 00075 00076 00077 $this->_table =& Dataface_Table::loadTable($tablename); 00078 00079 $this->_data = array(); 00080 if ( isset( $query['-cursor'] ) ){ 00081 $this->_data['cursor'] = $query['-cursor']; 00082 } else { 00083 $this->_data['cursor'] = 0; 00084 } 00085 00086 if ( isset( $query['-skip'] ) ){ 00087 $this->_data['start'] = $query['-skip']; 00088 } else { 00089 $this->_data['start'] = 0; 00090 } 00091 00092 if ( isset( $query['-limit'] ) ){ 00093 $this->_data['end'] = $this->_data['start'] + $query['-limit']-1; 00094 $this->_data['limit'] = $query['-limit']; 00095 } else { 00096 $this->_data['end'] = $this->_data['start'] + $GLOBALS['Dataface_QueryTool_limit']-1; 00097 $this->_data['limit'] = $GLOBALS['Dataface_QueryTool_limit']; 00098 00099 } 00100 00101 $tableKeyNames = array_keys($this->_table->keys()); 00102 if ( count($tableKeyNames) <= 0 ) throw new Exception("The table '$tablename' has no primary key. Please add one.", E_USER_ERROR); 00103 00104 $firstKeyName = $tableKeyNames[0]; 00105 00106 $cache =& $this->staticCache(); 00107 $sql = "select count(`$firstKeyName`) from `$tablename`"; 00108 00109 if ( isset($cache[$sql]) ) $this->_data['cardinality'] = $cache[$sql]; 00110 else { 00111 $res = $this->dbObj->query( $sql, $this->_db,null, true /*as array*/); 00112 if ( !$res and !is_array($res) ) throw new Exception("We had a problem with the query $sql.", E_USER_ERROR); 00113 00114 $this->_data['cardinality'] = reset($res[0]); 00115 $cache[$sql] = $this->_data['cardinality']; 00116 } 00117 00118 $builder = new Dataface_QueryBuilder( $tablename, $this->_query); 00119 $builder->selectMetaData = true; 00120 $sql = $builder->select_num_rows(); 00121 if ( isset($cache[$sql]) ){ 00122 $this->_data['found'] = $cache[$sql]; 00123 } else { 00124 00125 $res = $this->dbObj->query( $sql, $this->_db,null, true /*as array*/); 00126 if ( !$res and !is_array($res) ){ 00127 throw new Exception(mysql_error($this->_db).$sql, E_USER_ERROR); 00128 } 00129 $this->_data['found'] = array_shift($res[0]);//mysql_fetch_row( $res ); 00130 $cache[$sql] = $this->_data['found']; 00131 } 00132 00133 if ( $this->_data['end'] > $this->_data['found']-1 ){ 00134 $this->_data['end'] = $this->_data['found']-1; 00135 } 00136 if ( $this->_data['start'] > $this->_data['found'] ){ 00137 $this->_data['start'] = $this->_data['found']; 00138 } 00139 00140 00141 } 00142 00143 function getTitles($ordered=true, $genericKeys = false, $ignoreLimit=false){ 00144 $app =& Dataface_Application::getInstance(); 00145 if ( !isset($this->_titles[$ordered][$genericKeys][$ignoreLimit]) ){ 00146 $titleColumn = $this->_table->titleColumn(); 00147 00148 $keys = array_keys($this->_table->keys()); 00149 if ( !is_array($keys) || count($keys) == 0 ){ 00150 throw new Exception( 00151 df_translate( 00152 'No primary key defined', 00153 'There is no primary key defined on table "'.$this->_table->tablename.'". Please define a primary key.', 00154 array('table'=>$this->_table->tablename, 'stack_trace'=>'') 00155 ), 00156 E_USER_ERROR 00157 ); 00158 } 00159 $len = strlen($titleColumn); 00160 if ( $titleColumn{$len-1} != ')' and $titleColumn{$len-1} != '`') $titleColumn = '`'.$titleColumn.'`'; 00161 00162 $builder = new Dataface_QueryBuilder( $this->_tablename, $this->_query); 00163 $builder->action = 'select'; 00164 $from = $builder->_from(); 00165 $sql = "SELECT `".implode('`,`',$keys)."`,$titleColumn as `__titleColumn__` $from"; 00166 $where = $builder->_where(); 00167 $where = $builder->_secure($where); 00168 $limit = $builder->_limit(); 00169 if ( strlen($where)>0 ){ 00170 $sql .= " $where"; 00171 } 00172 if ( $ordered ){ 00173 $sql .= " ORDER BY `__titleColumn__`"; 00174 } else { 00175 $sql .= $builder->_orderby(); 00176 } 00177 if ( strlen($limit)>0 and !$ignoreLimit ){ 00178 $sql .= " $limit"; 00179 } else if ( !$ignoreLimit) { 00180 $sql .= " LIMIT 250"; 00181 } 00182 $res = $this->dbObj->query($sql, $this->_table->db, null,true /* as array */); 00183 if ( !$res and !is_array($res) ){ 00184 $app->refreshSchemas($this->_table->tablename); 00185 // updates meta tables such as workflow tables to make sure that they 00186 // are up to date. 00187 $res = $this->dbObj->query($sql, $this->_table->db,null, true /* as array */); 00188 if ( !$res and !is_array($res) ) 00189 throw new Exception( 00190 df_translate( 00191 'scripts.Dataface.QueryTool.getTitles.ERROR_ERROR_RETRIEVING_TITLES', 00192 "Error retrieving title from database in Dataface_QueryTool::getTitles(): " 00193 ) 00194 .$sql.mysql_error($this->_table->db), E_USER_ERROR); 00195 } 00196 $titles = array(); 00197 //while ( $row = mysql_fetch_row($res) ){ 00198 foreach ( $res as $row ){ 00199 $title = array_pop($row); 00200 if ( !$genericKeys) { 00201 $keyvals = array(); 00202 reset($keys); 00203 while ( sizeof($row)>0 ){ 00204 $keyvals[current($keys)] = array_shift($row); 00205 next($keys); 00206 } 00207 00208 $keystr = ''; 00209 foreach ($keyvals as $keykey=>$keyval){ 00210 $keystr .= urlencode($keykey)."=".urlencode($keyval)."&"; 00211 } 00212 $keystr = substr($keystr, 0, strlen($keystr)-1); 00213 $titles[$keystr] = $title; 00214 } else { 00215 $titles[] = $title; 00216 } 00217 00218 } 00219 //@mysql_free_result($res); 00220 00221 $this->_titles[$ordered][$genericKeys][$ignoreLimit] =& $titles; 00222 } 00223 00224 return $this->_titles[$ordered][$genericKeys][$ignoreLimit]; 00225 } 00226 00235 function loadSet($columns='', $loadText=false, $loadBlobs=false, $preview=true){ 00236 $app =& Dataface_Application::getInstance(); 00237 //It turns out that QueryBuilder handles whether or not blobs should be loaded so we won't worry about that here. 00238 $loadText=true; 00239 $loadBlobs=true; 00240 00241 $fields = $this->_table->fields(false, true); 00242 $fieldnames = array_keys($fields); 00243 $builder = new Dataface_QueryBuilder($this->_tablename, $this->_query); 00244 $builder->selectMetaData = true; 00245 // We set selectMetaData true so that the field lengths will be loaded as well. 00246 // This is especially useful for blob fields, since we don't load blobs - but 00247 // we still want to know th size of the blob. 00248 00249 // initialize the loaded mask 00250 if ( !isset( $this->_data['loaded'] ) ){ 00251 $this->_data['loaded'] = array(); 00252 foreach ($fieldnames as $fieldname){ 00253 $this->_data['loaded'][$fieldname] = false; 00254 } 00255 } 00256 $loaded =& $this->_data['loaded']; 00257 00258 // figure out which columns still need to be loaded. 00259 $cols = array(); 00260 if ( is_array($columns) ){ 00261 $cols = $columns; 00262 } else { 00263 foreach ($fieldnames as $col){ 00264 if ( $loaded[$col] ) continue; 00265 $cols[] = $col; 00266 } 00267 } 00268 00269 if ( sizeof( $cols ) > 0 ){ 00270 // we need to load a couple of columns 00271 $tablekeys = array_keys($this->_table->keys()); 00272 $select_cols = array_merge($cols, $tablekeys); 00273 $sql = $builder->select($select_cols, array(), false, null, $preview); 00274 00275 00276 $res = $this->dbObj->query( $sql, $this->_db, null, true/*as array*/); 00277 if ( !$res and !is_array($res) ){ 00278 $app->refreshSchemas($this->_table->tablename); 00279 $res = $this->dbObj->query( $sql, $this->_db, null, true/*as array*/); 00280 if ( !$res and !is_array($res) ) 00281 00282 throw new Exception( 00283 df_translate( 00284 'scripts.Dataface.QueryTool.loadSet.ERROR_LOADING_RECORDS', 00285 "Error loading records in Dataface_QueryTool::loadSet(): " 00286 ) 00287 .mysql_error($this->_db)."\n<br>".$sql, E_USER_ERROR); 00288 } 00289 if ( !isset( $this->_data['start'] ) ) 00290 $this->_data['start'] = $this->_query['-skip']; 00291 if ( !isset( $this->_data['end'] ) ) 00292 $this->_data['end'] = $this->_query['-skip'] + count($res);//mysql_num_rows($res); 00293 00294 if ( !isset( $this->_data['data'] ) ){ 00295 $this->_data['data'] = array(); 00296 $this->_data['indexedData'] = array(); 00297 } 00298 00299 $fieldnames = array_keys( $this->_table->fields(false, true) ); 00300 00301 foreach ( $res as $row){ 00302 $key=''; 00303 foreach ($tablekeys as $name){ 00304 $key .= $row[$name]; 00305 } 00306 00307 foreach ($row as $att=>$attval){ 00308 if ( !in_array($att, $fieldnames) and strpos($att,'__')!== 0 ){ 00309 unset( $row[$att] ); 00310 } 00311 } 00312 00313 if ( !isset( $this->_data['data'][$key] ) ){ 00314 $this->_data['data'][$key] = $row; 00315 $this->_data['indexedData'][] =& $this->_data['data'][$key]; 00316 } else { 00317 foreach ($cols as $col){ 00318 $this->_data['data'][$key][$col] = $row[$col]; 00319 } 00320 } 00321 } 00322 //@mysql_free_result($res); 00323 00324 foreach ($cols as $col){ 00325 $loaded[$col] = true; 00326 } 00327 00328 00329 } 00330 $cache =& $this->staticCache(); 00331 if (!isset( $this->_data['found'] ) ){ 00332 00333 $sql = $builder->select_num_rows(); 00334 00335 if ( isset($cache[$sql]) ){ 00336 $this->_data['found'] = $cache[$sql]; 00337 } else { 00338 $res = $this->dbObj->query( $sql, $this->_db,null, true /*as array*/); 00339 $this->_data['found'] = array_shift($res[0]); 00340 $cache[$sql] = $this->_data['found']; 00341 } 00342 00343 } 00344 00345 if ( !isset( $this->_data['cardinality'] ) ){ 00346 $tableKeyNames = array_keys($this->_table->keys()); 00347 if ( count($tableKeyNames) <= 0 ) throw new Exception("The table '$tablename' has no primary key. Please add one.", E_USER_ERROR); 00348 00349 $firstKeyName = $tableKeyNames[0]; 00350 $sql = "select count(`$firstKeyName`) from `".$this->_tablename.'`'; 00351 00352 if ( isset($cache[$sql]) ) $this->_data['cardinality'] = $cache[$sql]; 00353 else { 00354 $res = $this->dbObj->query( $sql, $this->_db,null, true /*as array*/); 00355 $this->_data['cardinality'] = array_shift($res[0]); 00356 $cache[$sql] = $this->_data['cardinality']; 00357 } 00358 00359 00360 } 00361 00362 return true; 00363 00364 00365 } 00366 00367 function &loadCurrent($columns=null, $loadText=true, $loadBlobs=false, $loadPasswords=false){ 00368 $app =& Dataface_Application::getInstance(); 00369 $false = false; // boolean placeholders for values needing to be returned by reference 00370 $true = true; 00371 00372 if ( $this->_currentRecord === null ){ 00373 //require_once 'Dataface/IO.php'; 00374 //$io = new Dataface_IO($this->_table->tablename); 00375 //$query = array_merge( $this->_query, array('-skip'=>$this->_data['cursor'], '-limit'=>1) ); 00376 $this->_currentRecord = new Dataface_Record($this->_table->tablename, array()); 00377 //$io->read($query, $this->_currentRecord); 00378 00379 } 00380 //return $this->_currentRecord; 00381 00382 00383 $unloaded = array(); 00384 $fields =& $this->_table->fields(false, true); 00385 if ( $columns === null ) { 00386 $names = array_keys($fields); 00387 } else { 00388 $names = $columns; 00389 } 00390 00391 foreach ($names as $name){ 00392 if ( !$this->_currentRecord->isLoaded($name) ){ 00393 if ( !$loadText and $this->_table->isText($name) ) continue; 00394 if ( !$loadBlobs and $this->_table->isBlob($name) ) continue; 00395 if ( !$loadPasswords and $this->_table->isPassword($name) ) continue; 00396 $unloaded[] = $name; 00397 } 00398 } 00399 00400 if ( sizeof( $unloaded ) > 0 ){ 00401 00402 $query = array_merge( $this->_query, array('-skip'=>$this->_data['cursor'], '-limit'=>1) ); 00403 $builder = new Dataface_QueryBuilder( $this->_tablename, $query); 00404 $builder->selectMetaData = true; 00405 $builder->_omitBlobs = false; 00406 00407 $sql = $builder->select($unloaded); 00408 //echo $sql; 00409 if ( PEAR::isError($sql) ){ 00410 throw new Exception($sql->toString(), E_USER_ERROR); 00411 } 00412 00413 //echo $sql; 00414 $res = $this->dbObj->query($sql, $this->_db,null, true /* as array */); 00415 if ( !$res and !is_array($res) ){ 00416 $app->refreshSchemas($this->_table->tablename); 00417 $res = $this->dbObj->query($sql, $this->_db, null,true /* as array */); 00418 if ( !$res and !is_array($res) ){ 00419 error_log(df_translate('scripts.Dataface.QueryTool.loadCurrent.ERROR_COULD_NOT_LOAD_CURRENT_RECORD',"Error: Could not load current record: ").mysql_error( $this->_db)."\n$sql"); 00420 throw new Exception("Failed to load current record due to an SQL error"); 00421 00422 } 00423 } 00424 if (count($res) <= 0 ){ 00425 return $false; 00426 } 00427 $row = $res[0]; //mysql_fetch_assoc($res); 00428 //@mysql_free_result($row); 00429 $this->_currentRecord = new Dataface_Record($this->_table->tablename, $row); 00430 //$this->_table->setValues($row); 00431 //$this->_table->setSnapshot(); 00432 //$this->_table->deserialize(); 00433 } 00434 00435 return $this->_currentRecord; 00436 00437 00438 } 00439 00440 00441 function found(){ 00442 if (!isset( $this->_data['found'] ) ){ 00443 $cache =& $this->staticCache(); 00444 $builder = new Dataface_QueryBuilder($this->_tablename,$this->_query); 00445 $sql = $builder->select_num_rows(); 00446 if ( isset($cache[$sql]) ) $this->_data['found'] = $cache[$sql]; 00447 else { 00448 $res = $this->dbObj->query( $sql, $this->_db, null,true /*as array*/); 00449 $this->_data['found'] = array_shift($res[0]); 00450 $cache[$sql] = $this->_data['found']; 00451 } 00452 00453 } 00454 return $this->_data['found']; 00455 00456 } 00457 00458 00459 function cardinality(){ 00460 if ( !isset( $this->_data['cardinality'] ) ){ 00461 $cache =& $this->staticCache(); 00462 $tableKeyNames = array_keys($this->_table->keys()); 00463 if ( count($tableKeyNames) <= 0 ) throw new Exception("The table '$tablename' has no primary key. Please add one.", E_USER_ERROR); 00464 00465 $firstKeyName = $tableKeyNames[0]; 00466 $sql = "select count(`$firstKeyName`) from ".$this->_tablename; 00467 if ( isset($cache[$sql]) ) $this->_data['cardinality'] = $cache[$sql]; 00468 else { 00469 $res = $this->dbObj->query( $sql, $this->_db,null, true /*as array*/); 00470 $this->_data['cardinality'] = array_shift($res[0]); 00471 $cache[$sql] = $this->_data['cardinality']; 00472 } 00473 00474 00475 } 00476 return $this->_data['cardinality']; 00477 } 00478 00479 function start(){ 00480 return $this->_data['start']; 00481 } 00482 function end(){ 00483 return $this->_data['end']; 00484 } 00485 00486 function &data(){ 00487 return $this->_data['data']; 00488 } 00489 00490 function &iterator(){ 00491 $it = new Dataface_RecordIterator($this->_tablename, $this->data()); 00492 return $it; 00493 } 00494 00495 function getRecordsArray(){ 00496 00497 $records = array(); 00498 $it = $this->iterator(); 00499 if ( PEAR::isError($it) )return $it; 00500 while ($it->hasNext()){ 00501 $records[] = $it->next(); 00502 } 00503 return $records; 00504 } 00505 00506 function limit(){ 00507 return $this->_data['limit']; 00508 } 00509 00510 00511 function cursor(){ 00512 return $this->_data['cursor']; 00513 } 00514 00515 function &indexedData(){ 00516 return $this->_data['indexedData']; 00517 } 00518 00519 public static function &loadResult($tablename, $db=null, $query=''){ 00520 if ( $db === null and defined('DATAFACE_DB_HANDLE') ) $db = DATAFACE_DB_HANDLE; 00521 if ( !isset( $resultDescriptors ) ){ 00522 static $resultDescriptors = array(); 00523 } 00524 00525 if ( is_array($query) and @$query['--no-query'] ){ 00526 $out = new Dataface_QueryTool_Null($tablename, $db, $query); 00527 return $out; 00528 } 00529 00530 if ( !isset( $resultDescriptors[$tablename] ) ){ 00531 $resultDescriptors[$tablename] = new Dataface_QueryTool($tablename, $db , $query); 00532 } 00533 return $resultDescriptors[$tablename]; 00534 } 00535 00536 } 00537 00538 00539 class Dataface_QueryTool_Null extends Dataface_QueryTool { 00540 00541 function &staticCache(){ 00542 static $cache = 0; 00543 if ( $cache === 0 ){ 00544 $cache = array(); 00545 } 00546 return $cache; 00547 } 00548 00549 00556 function __construct($tablename, $db=null, $query=null){ 00557 00558 00559 00560 } 00561 00562 function getTitles($ordered=true, $genericKeys = false, $ignoreLimit=false){ 00563 return array(); 00564 } 00565 00574 function loadSet($columns='', $loadText=false, $loadBlobs=false, $preview=true){ 00575 return true; 00576 00577 00578 } 00579 00580 function &loadCurrent($columns=null, $loadText=true, $loadBlobs=false, $loadPasswords=false){ 00581 return null; 00582 00583 00584 } 00585 00586 00587 function found(){ 00588 return 0; 00589 00590 } 00591 00592 00593 function cardinality(){ 00594 return 0; 00595 } 00596 00597 function start(){ 00598 return 0; 00599 } 00600 function end(){ 00601 return 0; 00602 } 00603 00604 function &data(){ 00605 return array(); 00606 } 00607 00608 function &iterator(){ 00609 $it = new Dataface_RecordIterator($this->_tablename, $this->data()); 00610 return $it; 00611 } 00612 00613 function getRecordsArray(){ 00614 return array(); 00615 } 00616 00617 function limit(){ 00618 return 0; 00619 } 00620 00621 00622 function cursor(){ 00623 return 0; 00624 } 00625 00626 function &indexedData(){ 00627 return array(); 00628 } 00629 00630 00631 }