Xataface 2.0
Xataface Application Framework
Dataface/QueryTranslator.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  */
00030 class Dataface_QueryTranslator {
00031         var $app;
00032         var $_tableNames = array(); // [Alias -> Name]
00033         var $_tableAliases = array();// [Name -> Alias]
00034         var $_tables = array();          // [Dataface_Table]
00035         
00036         var $_tableNames_tr = array(); // translated table names : [Alias -> Name]
00037         var $_tableAliases_tr = array(); // translated table aliases : [Name -> Alias]
00038         var $_tableAliasTranslationMap = array(); // maps translated table alias to original table alias
00039         
00040         var $_columnTranslationMap = array();
00041         
00042         var $_tableTranslations = null;
00043         
00044         var $_lang = null;
00045         var $_data;     // the parsed data after SQL parser has parsed the query.
00046         var $_data_translated; // the translated data.
00047         
00048         var $_parser;
00049         var $_compiler;
00050         var $_query;
00051         
00052         var $parentContext;
00053         
00054         
00068         function Dataface_QueryTranslator($lang=null){
00069                 $this->app =& Dataface_Application::getInstance();
00070                 if ( !isset($lang) ) $lang = $this->app->_conf['lang'];
00071                 $this->_lang = $lang;
00072                 
00073                 //if ( !@$this->app->_conf['default_language_no_fallback'] or $this->app->_conf['default_language'] != $lang ){
00074                         // In Dataface 0.6.10 the default behavior of the query translator was
00075                         // changed so that default language queries are not changed.  This 
00076                         // behavior can be reversed by adding the default_language_no_fallback=1
00077                         // flag to the conf.ini file.
00078                         
00079                         import('SQL/Parser.php');
00080                         $this->_parser = new SQL_Parser( null, 'MySQL');
00081                         import('SQL/Compiler.php');
00082                         $this->_compiler =& SQL_Compiler::newInstance('mysql');
00083                         $this->_compiler->version = 2;
00084                 //}
00085                 
00086         }
00087         
00099         function setParentContext(&$translator){
00100                 $this->parentContext =& $translator;
00101         }
00102         
00103         function translateQuery($query, $lang=null, $compile = true){
00104                 //echo "Translating query: ".$query;
00105                 if ( !is_array($query) ){
00106                                 
00107                         
00108                         $query = trim($query);
00109                         $this->_query = $query;
00110                         $command = strtolower(substr($query, 0, strpos($query, ' ')));
00111                 } else {
00112                         $command = $query['command'];
00113                 }
00114                 
00115                 // Reset all of the private variables that are used for temp storage
00116                 // while parsing.
00117                 unset($this->_tableNames);
00118                 unset($this->_tableAliases);
00119                 unset($this->_tableAliasTranslationMap);
00120                 unset($this->_columnTranslationMap);
00121                 unset($this->_data);
00122                 unset($this->_talbeNames_tr);
00123                 unset($this->_tableAliases_tr);
00124                 $this->_tableNames = array();
00125                 $this->_tableAliases = array();
00126                 $this->_tableAliasTranslationMap = array();
00127                 $this->_columnTranslationMap = array();
00128                 if ( isset($lang) ) $this->_lang = $lang;
00129                 
00130                 // In Dataface 0.6.10 we changed the default behavior of the translations so 
00131                 // that the translation table is not used for the default language.
00132                 // This can be reversed by adding the default_language_no_fallback=1 flag
00133                 // to the conf.ini file.
00134                 // If the flag has not been added, then queries in the default language
00135                 // will be returned unchanged.
00136                 if ( !@$this->app->_conf['default_language_no_fallback'] and ($this->_lang == $this->app->_conf['default_language']) ){
00137 
00138                         return array($query);
00139                 } 
00140                 if ( is_array($query) ){
00141                         $this->_data = $query;
00142                 } else {
00143                         $this->_data = $this->_parser->parse($query);
00144                 }
00145                 if ( PEAR::isError($this->_data) ) return $this->_data;
00146                 $this->_tableNames_tr = array();
00147                 $this->_tableAliases_tr = array();
00148 
00149                 
00150                 
00151                 
00152                 
00153                 switch ($command){
00154                         case 'select':  return $this->translateSelectQuery($query,$lang, $compile);
00155                         case 'update':  return $this->translateUpdateQuery($query,$lang, $compile);
00156                         case 'insert':  return $this->translateInsertQuery($query,$lang, $compile);
00157                         case 'delete':  return $this->translateDeleteQuery($query,$lang, $compile);
00158                         default:                return PEAR::raiseError(
00159                                                                         df_translate(
00160                                                                                 'scripts.Dataface.QueryTranslator.translateQuery.ERROR_INVALID_QUERY',
00161                                                                                 "Invalid query attempted to be translated.  Expected select, update, or insert query but received: ".$query,
00162                                                                                 array('query'=>$query)
00163                                                                                 )
00164                                                                         );
00165                 }
00166         }
00167         
00168         
00169         
00170         function translateSelectQuery($query, $lang = null, $compile = true){
00171                 //echo "Translating $query";
00172                 // Make a short ref to the parsed data structure of the query
00173                 $d =& $this->_data;
00174                 
00175                 // fill in the tableNames:
00176                 $numTables = count($d['tables']);       // number of tables in the query
00177                 for ($i=0; $i<$numTables; $i++){
00178                         if ( $d['tables'][$i]['type'] == 'ident' ){
00179                                 $tname = $d['tables'][$i]['value'];
00180                                 $talias = $d['tables'][$i]['alias'];
00181                                 if ( !$talias ) $talias = $tname;
00182                         } else {
00183                                 // This table is a subselect - we won't keep this in the tables array
00184                                 $translator = new Dataface_QueryTranslator($this->_lang);
00185                                 $d['tables'][$i]['value'] = $translator->translateQuery($d['tables'][$i]['value'], null, false);
00186                                 continue;
00187                         }
00188                         
00189                         $this->_tableNames[$talias] = $tname;
00190                         $this->_tableAliases[$tname] = $talias;
00191                         if (!isset( $this->_tables[$tname] ) ){
00192                                 $this->_tables[$tname] =& Dataface_Table::loadTable($tname);
00193                         }
00194                 }
00195                 
00196                 if ( isset( $this->parentContext) ){    
00197                         foreach ( array_keys($this->parentContext->_tables) as $tablename ){
00198                                 if ( !isset($this->_tables[$tablename]) ){
00199                                         $this->_tables[$tablename] =& $this->parentContext->_tables[$tablename];
00200                                         $this->_tableAliases[$tablename] = $this->parentContext->_tablesAliases[$tablename];
00201                                         $this->_tableNames[$this->_tableAliases[$tablename]] = $tablename;
00202                                 }
00203                         }
00204                 }
00205                 
00206                 
00207                 
00208                 // Prepare the translated data array:
00209                 $this->_data_translated = $d;
00210                         // Placeholder for the data structure for the translated query
00211                 $this->_data_translated['columns'] = array();
00212                 if ( !isset( $d['table_join'] ) ) $this->_data_translated['table_join'] = array();
00213                         // If there were no joins, we initialize it.
00214                         
00215                 // Translate the column names
00216                 if ( isset( $d['columns'] ) and is_array($d['columns']) ) {
00217                         $numCols = count($d['columns']);
00218                         for ($i=0; $i<$numCols; $i++){
00219                                 $currColumn  = $d['columns'][$i];
00220                                 //$this->_data_translated['columns'][] =& $currColumn;
00221                                 $this->translateColumn($currColumn);
00222                                 unset($currColumn);
00223                         }
00224                         
00225                 }
00226                 
00227                 
00228                 // Translate the where clause
00229                 if ( isset($d['where_clause']) ){
00230                         $this->translateClause($this->_data_translated['where_clause']);
00231                 }
00232                 
00233                 // Translate the join clause
00234                 if ( isset($d['table_join_clause']) ){
00235                         $numClauses = count($d['table_join_clause']);
00236                         for ($i=0; $i<$numClauses; $i++){
00237                                 
00238                                 $this->translateClause($this->_data_translated['table_join_clause'][$i]);
00239                         }
00240                 }
00241                 
00242                 // Translate order by clause
00243                 if ( isset($d['sort_order']) ){
00244                         $numClauses = count($d['sort_order']);
00245                         for ( $i=0; $i<$numClauses;$i++){
00246                                 $this->translateClause($this->_data_translated['sort_order'][$i]);
00247                         }
00248                 }
00249                 
00250                 if ( $compile ){
00251                         $out = array($this->_compiler->compile( $this->_data_translated));
00252                         //echo "Translated as: ";print_r($out);
00253                         return $out;
00254                 } else {
00255                         return $this->_data_translated;
00256                 }
00257                 
00258                 
00259         }
00260         
00261 
00262         
00272         function translateColumn(&$col, $column_alias=null, $update=true){
00273                 if ( is_array($col) ){
00274                         // For backwards compatability it is possible for $col to simply
00275                         // be the name of a column.  Fron now on, however, $col will be 
00276                         // an associative array of column information of the form:
00277                         // 'type' => ident|func
00278                         // 'table' => ..
00279                         // 'value' => ..
00280                         // 'alias' => ..
00281                         $columnInfo =& $col;
00282                         unset($col);
00283                         switch ( $columnInfo['type'] ){
00284                                 case 'glob':
00285                                 case 'ident':
00286                                         $col = $columnInfo['value'];
00287                                         if ( $columnInfo['table'] ) $col = $columnInfo['table'].'.'.$col;
00288                                         $column_alias = $columnInfo['alias'];
00289                                         break;
00290                                 
00291                                 case 'func':
00292                                         //print_r($columnInfo['value']);
00293                                         $this->translateFunction($columnInfo['value'], $update);
00294                                         if ( $update ){
00295                                                 $this->_data_translated['columns'][] =& $columnInfo;
00296                                                 return true;
00297                                         } else {
00298                                                 // This shouldn't happen.
00299                                         }
00300                                         break;
00301                         }
00302                 }
00303                 if ( $update && preg_match('/\.{0,1}\*$/', $col) ){
00304                         // this is a glob column.
00305                         $expandedGlob = $this->expandGlob($col);
00306                         foreach ( $expandedGlob as $globCol){
00307                                 $currColumnTable = null;
00308                                 $currColumnName = null;
00309                                 $currColumnArr = explode('.', $globCol);
00310                                 if ( count($currColumnArr) > 1){
00311                                         $currColumnName = $currColumnArr[1];
00312                                         $currColumnTable = $currColumnArr[0];
00313                                 } else {
00314                                         $currColumnName = $currColumnArr[0];
00315                                         $currColumnTable = '';
00316                                 }
00317                                 $currColumn = array('type'=>'ident','table'=>$currColumnTable,'value'=>$currColumnName, 'alias'=>'');
00318                                 //$this->_data_translated['columns'][] =& $currColumn;
00319                                 $this->translateColumn($currColumn);
00320                                 unset($currColumn);
00321                         }
00322                         return true;
00323                 } 
00324                 
00325                 $originalColumnName = $col;
00326                 $tablename = null;
00327                 if ( strpos($col, '.') !== false ) {
00328                         // this column has table portion
00329                         list($alias,$col) = explode('.', $col);
00330                         
00331                         if ( isset($this->_tableNames[$alias]) ){
00332                                 $tablename = $this->_tableNames[$alias];
00333                         } 
00334                 } else {
00335                         $alias = '';
00336                         foreach ( array_keys($this->_tables) as $tableKey){
00337                                 if ( isset($this->_tables[$tableKey]->_fields[$col]) ){
00338                                         $tablename = $this->_tables[$tableKey]->tablename;
00339                                         break;
00340                                 }
00341                         }
00342 
00343                 }
00344 
00345                 if ( !isset($tablename) ){
00346                         
00347                         // This column is not associated with a table.  This means
00348                         // that it could be referencing a subselect - which would 
00349                         // already have been translated, so we don't need to do any
00350                         // translations - or it could be that the column doesn't exist.
00351                         // In either case we just leave it alone and MySQL can return
00352                         // an error in the latter case.
00353                         if ( !$update ){
00354                                 
00355                                 return array('column_names'=>array(($alias ? $alias.'.' : '').$col), 'column_aliases'=>array($column_alias));
00356                         } else {
00357                                 if ( isset( $columnInfo ) ){
00358                                         $this->_data_translated['columns'][] = $columnInfo;
00359                                 } 
00360                                 return false;
00361                         }
00362                 } else if ( isset( $this->_tableAliases[$tablename]) and !$alias  ){
00363                         $alias = $this->_tableAliases[$tablename];
00364                 }
00365                 
00366                 $table =& $this->_tables[$tablename];
00367                 $translation = $table->getTranslation($this->_lang);
00368                 if ( !isset($translation) or !in_array($col, $translation) or in_array($col, array_keys($table->keys()) ) ){
00369                         // there is no translation for this field
00370                         // so we do nothing here
00371                         if ( $update ){
00372                                 //$this->_data_translated['column_names'][] = ($alias ? $alias : $tablename).'.'. $col;
00373                                 //$this->_data_translated['column_aliases'][] = $column_alias;
00374                                 if ( isset($columnInfo) ){
00375                                         if ( !$columnInfo['table'] ) $columnInfo['table'] = $tablename;
00376                                         if ( $alias ) $columnInfo['table'] = $alias;
00377                                         $this->_data_translated['columns'][] = $columnInfo;
00378                                 }
00379                                 return false;
00380                         } else {
00381                                 return array('column_names'=>array(($alias ? $alias : $tablename).'.'.$col), 'column_aliases'=>array($column_alias));
00382                         }
00383                 } else {
00384                         // the table has a translation
00385                 
00386                         // the column has a translation
00387                         $old_alias = $alias;
00388                         if ( !$alias ){
00389                                 $alias = $tablename.'__'.$this->_lang;
00390                                 
00391                         } else {
00392                                 $alias = $alias.'__'.$this->_lang;
00393                         }
00394                         if ( !isset($this->_tableNames_tr[$alias] ) ){
00395                                 // If the translation table hasn't been recorded yet, let's do that now
00396                                 $this->_tableNames_tr[$alias] = $tablename.'_'.$this->_lang;
00397                         }
00398                         if ( !isset($this->_tableAliases_tr[$tablename.'_'.$this->_lang]) ){
00399                                 $this->_tableAliases_tr[$tablename.'_'.$this->_lang] = $tablename;
00400                         }
00401                         if ( !isset($this->_tableAliasTranslationMap[$alias]) ){
00402                                 $this->_tableAliasTranslationMap[$alias] = $old_alias;
00403                         }
00404                         
00405                         // Now we add the join clause for the translation table (if necessary)
00406                         if ( !in_array( $alias, $this->_data_translated['table_aliases'] ) ){
00407                                 $this->_data_translated['table_names'][] = $tablename.'_'.$this->_lang;
00408                                 $this->_data_translated['table_aliases'][] = $alias;
00409                                 $this->_data_translated['tables'][] = array('type'=>'ident','value'=>$tablename.'_'.$this->_lang, 'alias'=>$alias);
00410                                 $this->_data_translated['table_join'][] = 'left join';
00411                                 $join_clause = null;
00412                                 foreach ( array_keys($this->_tables[$tablename]->keys()) as $keyName){
00413                                         $temp = array(
00414                                                 'arg_1'=>array(
00415                                                         'value'=> ( $old_alias ? $old_alias : $tablename).'.'.$keyName,
00416                                                         'type'=> 'ident'
00417                                                         ),
00418                                                 'op'=>'=',
00419                                                 'arg_2'=>array(
00420                                                         'value'=> $alias.'.'.$keyName,
00421                                                         'type'=>'ident'
00422                                                         )
00423                                                 );
00424                                         if ( !isset($join_clause) ){
00425                                                 $join_clause =& $temp;
00426                                         } else {
00427                                                 $temp2 =& $join_clause;
00428                                                 unset($join_clause);
00429                                                 $join_clause = array(
00430                                                         'arg_1'=>&$temp2,
00431                                                         'op'=>'and',
00432                                                         'arg_2'=>&$temp
00433                                                         );
00434                                         }
00435                                         
00436                                         unset($temp);
00437                                         unset($temp2);
00438                                         
00439                                                 
00440                                 }
00441                                 $this->_data_translated['table_join_clause'][] = $join_clause;
00442                         }
00443                         
00444                         
00445                         
00446                         
00447                         // Now adjust the column name
00448                 
00449                         $func_struct = 
00450                                 array(
00451                                         'name'=>'ifnull',
00452                                         'args'=>array(
00453                                                 array(
00454                                                         'type'=>'ident',
00455                                                         'value'=>$alias.'.'.$col
00456                                                         ),
00457                                                 array(
00458                                                         'type'=>'ident',
00459                                                         'value'=>($old_alias ? $old_alias : $tablename).'.'.$col
00460                                                         ),
00461                                                 ),
00462                                         'alias'=>($column_alias ? $column_alias : $col)
00463                                         );
00464                         if ( $update && !isset( $this->_columnTranslationMap[$originalColumnName] ) ) {
00465                                 $this->_columnTranslationMap[$originalColumnName] = $func_struct['alias'];
00466                         }
00467                         if ( $update){
00468                                 if ( isset($columnInfo) ){
00469                                         $columnInfo['type'] = 'func';
00470                                         $columnInfo['table'] = '';
00471                                         $columnInfo['value'] = $func_struct;
00472                                         $columnInfo['alias'] = $func_struct['alias'];
00473                                         $this->_data_translated['columns'][] = $columnInfo;
00474                                         return true;
00475                                         //print_r($columnInfo);
00476                                 } else {
00477                                         $this->_data_translated['set_function'][] = $func_struct;
00478                                         return true;
00479                                 }
00480                         } else {
00481                                 return array('set_function'=>array($func_struct));
00482                         }       
00483                         
00484                 }
00485                 return true;
00486         }
00487         
00494         function translateFunction(&$func, $update=true){
00495                 if ( !isset( $func['args'] ) ) return false;
00496                 if ( !$update ) $new_func = $func;
00497                 foreach ( array_keys($func['args']) as $key){
00498                         $arg =& $func['args'][$key];
00499                         switch( $arg['type'] ){
00500                                 case 'ident':
00501                                                 $new_value = $this->translateColumn($func['args'][$key]['value'], null, false);
00502                                                 
00503                                                 if ( isset($new_value['set_function']) ) {
00504                                                         $new_arg = array('type'=>'function', 'value'=>$new_value['set_function'][0]);
00505                                                 } else if ( isset( $new_value['column_names'] ) ){
00506                                                         $new_arg = array('type'=>'ident', 'value'=>$new_value['column_names'][0]);
00507                                                 }
00508                                                 if ( !$update ){
00509                                                         $new_func['args'][$key] = $new_arg;
00510                                                 } else {
00511                                                         $func['args'][$key] = $new_arg;
00512                                                 }
00513                                                 break;
00514                                 
00515                                 case 'function':
00516                                                 if ( $update ) {
00517                                                         $this->translateFunction($func['args'][$key]['value'], true);
00518                                                 } else {
00519                                                         $this->translateFunction($new_func['args'][$key]['value'], true);
00520                                                 }
00521                                                 break;
00522                                                 
00523                         }
00524                         unset($arg);
00525                         
00526                 }
00527                 
00528                 if ( !$update ){
00529                         
00530                         return $new_func;
00531                 } else {
00532                         return true;
00533                 }
00534                         
00535         }
00536         
00541         function translateClause(&$clause){
00542                 if ( isset($clause['type']) ) $this->translateUnaryClause($clause);
00543                 else $this->translateBinaryClause($clause);
00544                                 
00545         }
00546         
00547         function translateBinaryClause(&$clause){
00548                 if ( !is_array($clause) ) return;
00549                 foreach ( array('arg_1','arg_2') as $arg ){
00550                         if ( !isset($clause[$arg]) ) continue;
00551                         $this->translateUnaryClause($clause[$arg]);
00552                 }
00553         }
00554         
00555         function translateUnaryClause(&$clause){
00556                 if ( !isset($clause['type']) ){
00557                         $this->translateClause($clause);
00558                 } else {
00559                         switch( $clause['type'] ){
00560                                 case 'ident':
00561                                         $new_value = $this->translateColumn($clause['value'], null, false);
00562                                         if ( is_array($new_value) and isset($new_value['set_function']) ) {
00563                                                 // the translation is a function
00564                                                 $clause['type'] = 'function';
00565                                                 $clause['value'] = $new_value['set_function'][0];
00566                                         } else {
00567                                                 $clause['value'] = $new_value['column_names'][0];
00568                                         }
00569                                         
00570                                         break;
00571                                 
00572                                 case 'function':
00573                                         $this->translateFunction($clause['value']);
00574                                         break;
00575                                         
00576                                 case 'subclause':
00577                                         $this->translateClause($clause['value']);
00578                                         break;
00579                                         
00580                                 case 'command':
00581                                 case 'subquery':
00582                                         $translator = new Dataface_QueryTranslator($this->_lang);
00583                                         $translator->setParentContext($this);
00584                                         $clause['value'] = $translator->translateQuery($clause['value'], $this->_lang, false);
00585                                         
00586                                         break;
00587                                 
00588                                 //case 'match':
00589                                 //      if ( isset($clause['value']) and is_array($clause['value']) ){
00590                                 //              $numClauses = count($clause['value']);
00591                                 //              for ($i=0; $i<$numClauses; $i++){
00592                                 //                      
00593                                 //                      
00594                                 //                      
00595                                 //              }
00596                                 //      }
00597                                 //      break;
00598                                         
00599                                         
00600                                         
00601                         }
00602                 }
00603                 
00604                 
00605         }
00606         
00607 
00608         
00613         function expandGlob($glob){
00614                 $numTables = count($this->_tableNames);
00615                 if ( strpos($glob,'.') !== false ){
00616                         // This is a glob of only a single table or alias
00617                         list($alias, $glob) = explode('.', $glob);
00618                         if ( isset( $this->_tableNames[$alias]) ){
00619                                 $out = array_keys($this->_tables[ $this->_tableNames[$alias] ]->fields() );
00620                                 $out2=array();
00621                                 foreach ($out as $col){
00622                                         $out2[] = $alias.'.'.$col;
00623                                 }
00624                                 return $out2;
00625                         } else {
00626                                 throw new Exception(
00627                                         df_translate(
00628                                                 'scripts.Dataface.QueryTranslator.expandGlob.ERROR_NONEXISTENT_TABLE',
00629                                                 "Attempt to expand glob for non-existent table '$alias'",
00630                                                 array('table'=>$alias)
00631                                                 ), E_USER_ERROR);
00632                         }
00633                 } else {
00634                         $fields = array();
00635                         foreach ( array_keys($this->_tableNames) as $alias ){
00636                                 $newfields = array_keys($this->_tables[ $this->_tableNames[ $alias ] ]->fields());
00637                                 foreach ( $newfields as $newfield ){
00638                                         //if ( $numTables > 1 ){
00639                                                 $fields[] = $alias.'.'.$newfield;
00640                                         //} else {
00641                                         //      $fields[] = $newfield;
00642                                         //}
00643                                 }
00644                         }
00645                         return $fields;
00646                 }
00647                         
00648         }
00649         
00657         function translateUpdateQuery($query){
00658                 /*
00659                  * This method translates an update query to be multilingual.
00660                  * It tries to be non-obtrusive in that only column names in the 
00661                  * update list are converted.  The where clause is left alone.
00662                  * As a consequence, this does not support multi-table updates.
00663                  */
00664                 
00665                 $d =& $this->_data;
00666                 $tableName = $d['table_names'][0];
00667                 if ( count($d['table_names']) > 1 ){
00668                         return PEAR::raiseError(
00669                                 df_translate(
00670                                         'scripts.Dataface.QueryTranslator.translateUpdateQuery.ERROR_MULTI_TABLE_UPDATE',
00671                                         'Failed to translate update query because the translator does not support multiple-table update syntax.'
00672                                         ), E_USER_ERROR);
00673                 }
00674                 
00675                 $table = Dataface_Table::loadTable($tableName);
00676                 $keys = array_keys($table->keys());
00677                         // Array of the names of fields the form the primary key of this table.
00678                 
00679                 $translation = $table->getTranslation($this->_lang);
00680                         // Array of column names that have a translation in this language.
00681                 
00682                 
00683                 if ( !isset( $translation ) ) $translation = array();
00684                         // If there is no translation we will just set the translation to 
00685                         // and empty array.  Even if there is no translation for the current
00686                         // language, we should still go through and parse the query in case 
00687                         // a key is being changed and other translations will need to be modified
00688                         // to maintaing foreign key integrity.
00689                         
00690                 // We initialize the data structure to store the update to the translation
00691                 // table.
00692                 $this->_data_translated = $d;   // to store update query to translation table
00693                 $this->_data_translated['column_names'] = array();
00694                 $this->_data_translated['values'] = array();
00695                 $this->_data_translated['table_names'] = array($tableName.($translation?'_'.$this->_lang:''));
00696                 
00697                 // Initialize the data structure to store the update to the original table
00698                 // after translated columns are removed.
00699                 $new_data = $d; // to store update query to base table
00700                 $new_data['column_names'] = array();
00701                 $new_data['values'] = array();
00702                 
00703                 // Initialize the data structure to store the update to the other translation
00704                 // tables in case the keys are changed.  This update is just to synchronize 
00705                 // the keys.
00706                 $keyChange_data = $d;
00707                 $keyChange_data['column_names'] = array();
00708                 $keyChange_data['values'] = array();
00709                         // if keys are updated, then they should be updated in all translation tables
00710                         // to remain consistent.
00711                 
00712                 $numCols = count($d['column_names']);
00713                 $translationRequired = false;
00714                 $originalRequired = false;
00715                 $keysChanged = false;
00716                 
00717                 for ($i=0; $i<$numCols; $i++ ){
00718                         $col = $d['column_names'][$i];
00719                         $value = $d['values'][$i];
00720                         if ( in_array($col, $keys) ){
00721                                 $originalRequired = true;
00722                                 $keysChanged = true;
00723                                 $this->_data_translated['column_names'][] = $col;
00724                                 $this->_data_translated['values'][] = $value;
00725                                 $new_data['column_names'][] = $col;
00726                                 $new_data['values'][] = $value;
00727                                 $keyChange_data['column_names'][] = $col;
00728                                 $keyChange_data['values'][] = $value;
00729                         } else if ( in_array($col, $translation)  ){
00730                                 $translationRequired = true;
00731                                 $this->_data_translated['column_names'][] = $col;
00732                                 $this->_data_translated['values'][] = $value;
00733                         } else {
00734                                 $originalRequired = true;
00735                                 $new_data['column_names'][] = $col;
00736                                 $new_data['values'][] = $value;
00737                         }
00738                 }
00739                 
00740                 if (!$translationRequired and !$keysChanged){
00741                         return array($query);
00742                 } else {
00743                         $queryKeys = $this->extractQuery($d);
00744                         if ( $translationRequired ){
00745                                 $out = array('insert ignore into `'.$tableName.'_'.$this->_lang.'` (`'.implode('`,`', array_keys($queryKeys)).'`) values (\''.implode('\',\'', array_values($queryKeys)).'\')');
00746                         } else {
00747                                 $out = array();
00748                         }
00749                         if ( $originalRequired ) $out[] = $this->_compiler->compile($new_data);
00750                         $out[] =  $this->_compiler->compile($this->_data_translated);
00751                         
00752                         if ( $keysChanged ){
00753                                 $translations = array_keys($table->getTranslations());
00754                                 foreach ( $translations as $tr ){
00755                                         if ( $tr == $this->_lang ) continue;
00756                                         $keyChange_data['table_names'] = array($tableName.'_'.$tr);
00757                                         $out[] = $this->_compiler->compile($keyChange_data);
00758                                 }
00759                         }
00760 
00761                         return $out;
00762                 }
00763                 
00764                 
00765                         
00766                 
00767                 
00768         }
00769         
00774         function extractQuery(&$data){
00775                 $w =& $data['where_clause'];
00776                 $out = array();
00777                 $this->extractQuery_rec($w, $out);
00778                 return $out;
00779         }
00780         
00781         function extractQuery_rec($clause, &$out){
00782                 if ( !is_array($clause)) return;
00783                 
00784                 if ( isset($clause['arg_1']     ) ){
00785                         switch ($clause['arg_1']['type']){
00786                                 case 'subclause':
00787                                         $this->extractQuery_rec($clause['arg_1'], $out);
00788                                         break;
00789                                 
00790                                 case 'ident':
00791                                         if ( in_array($clause['arg_2']['type'], array('int_val','real_val','text_val','null') )  and $clause['op'] == '='){
00792                                                 $out[$clause['arg_1']['value']] = $clause['arg_2']['value'];
00793                                         }
00794                                         break;
00795                                         
00796                         }
00797                 }
00798                 
00799                 
00800         }
00801         
00811         function translateInsertQuery($query){
00812 
00813                 /*
00814                  * This method translates an update query to be multilingual.
00815                  * It tries to be non-obtrusive in that only column names in the 
00816                  * update list are converted.  The where clause is left alone.
00817                  * As a consequence, this does not support multi-table updates.
00818                  */
00819                 $d =& $this->_data;
00820                 $tableName = $d['table_names'][0];
00821                 if ( count($d['table_names']) > 1 ){
00822                         return PEAR::raiseError(
00823                                 df_translate(
00824                                         'scripts.Dataface.QueryTranslator.translateUpdateQuery.ERROR_MULTI_TABLE_UPDATE',
00825                                         'Failed to translate update query because the translator does not support multiple-table update syntax.'
00826                                         ), E_USER_ERROR);
00827                 }
00828                 
00829                 $table = Dataface_Table::loadTable($tableName, null, false, true);
00830                 if ( PEAR::isError($table) ){
00831                         return array($query);
00832                 }
00833                 
00834                 $translation = $table->getTranslation($this->_lang);
00835                         // Array of column names that have a translation in this language.
00836                 
00837                 if ( !isset( $translation ) ) return array($query);
00838                         // there are no translations for this table, so we just return the query.
00839                         
00840                 // We initialize the data structure to store the update to the translation
00841                 // table.
00842                 $this->_data_translated = $d;   // to store update query to translation table
00843                 $this->_data_translated['column_names'] = array();
00844                 $this->_data_translated['values'] = array();
00845                 $this->_data_translated['table_names'] = array($tableName.'_'.$this->_lang);
00846                 
00847                 // Initialize the data structure to store the update to the original table
00848                 // after translated columns are removed.
00849                 $new_data = $d; // to store update query to base table
00850                 $new_data['column_names'] = array();
00851                 $new_data['values'] = array();
00852                 
00853                 
00854                 
00855                 $numCols = count($d['column_names']);
00856                 $translationRequired = false;
00857                 $originalRequired = true;       // for inserts the original is always required!
00858                 
00859                 if ( ($aif = $table->getAutoIncrementField())  ){
00860                         $new_data['column_names'][] = $this->_data_translated['column_names'][] = $aif;
00861                         $new_data['values'][] = $this->_data_translated['values'][] = array('type'=>'text_val', 'value'=>'%%%%%__MYSQL_INSERT_ID__%%%%%');
00862                         
00863                 }
00864                 
00865                 for ($i=0; $i<$numCols; $i++ ){
00866                         $col = $d['column_names'][$i];
00867                         $value = $d['values'][$i];
00868                         if ( in_array($col, $translation)){
00869                                 $translationRequired = true;
00870                                 $this->_data_translated['column_names'][] = $col;
00871                                 $this->_data_translated['values'][] = $value;
00872                         } 
00873                                 
00874                         $new_data['column_names'][] = $col;
00875                         $new_data['values'][] = $value;
00876                         
00877                 }
00878 
00879                 if (!$translationRequired){
00880                         $out = array($query);
00881                 } else {
00882                         $out = array();
00883                         
00884                         if ( $originalRequired ) $out[] = $this->_compiler->compile($new_data);
00885                         $out[] =  $this->_compiler->compile($this->_data_translated);
00886                         
00887                 }
00888 
00889                 return $out;
00890                 
00891         }
00892         
00898         function translateDeleteQuery($query){
00899                 $d =& $this->_data;
00900                 $out = array($this->_compiler->compile($d));
00901                 $table = Dataface_Table::loadTable($d['table_names'][0]);
00902                 foreach ( array_keys($table->getTranslations()) as $lang){
00903                         $d['table_names'][0] = $table->tablename.'_'.$lang;
00904                         $out[] = $this->_compiler->compile($d);
00905                 }
00906                 return $out;
00907         }
00908 }
 All Data Structures Namespaces Files Functions Variables Enumerations