Xataface 2.0
Xataface Application Framework
Dataface/QueryTool.php
Go to the documentation of this file.
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 }
 All Data Structures Namespaces Files Functions Variables Enumerations