![]() |
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 */ 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 }