|
Tripal 0.3b
|
00001 <?php 00002 /** 00003 * @file 00004 * The Tripal Core API 00005 * 00006 * This file provides the API needed for all other Tripal and Tripal dependent 00007 * modules. 00008 */ 00009 00010 /** 00011 * @defgroup tripal_api Tripal API 00012 * @{ 00013 * Provides an application programming interface (API) for Tripal 00014 * 00015 * The Tripal API currently provides generic insert/update/select functions for all chado content as 00016 * well as some module specific functions that insert/update/delete/select specific chado content. 00017 * 00018 * This API is currently in its infancy and some necessary functions might be missing. If you find 00019 * a missing function that you think should be included go to the sourceforge feature request 00020 * page and request it's inclusion in the API. Such feature requests with a working function 00021 * definition will be given priority. 00022 * @} 00023 */ 00024 00025 require_once "tripal_core.schema.api.inc"; 00026 00027 /** 00028 * @defgroup tripal_chado_api Core Module Chado API 00029 * @{ 00030 * Provides an application programming interface (API) to manage data withing the Chado database. 00031 * This includes functions for selecting, inserting, updating and deleting records 00032 * in Chado tables. The functions will ensure proper integrity contraints are met 00033 * for inserts and updates. 00034 * 00035 * Also, a set of functions is provided for creating template variables. First, 00036 * is the tripal_core_generate_chado_vars which is used to select one ore more 00037 * records from a table and return an array with foreign key relationships fully 00038 * populated. For example, if selecting a feature, the organism_id and type_id 00039 * would be present in the returned array as a nested array with their respective 00040 * foreign keys also nested. The only fields that are not included are text 00041 * fields (which may be very large) or many-to-many foreign key relationships. 00042 * However, these fields and relationships can be expanded using the 00043 * tripal_core_expand_chado_vars. 00044 * 00045 * When a row from a chado table is selected using these two functions, it provides 00046 * a way for users who want to cutomize Drupal template files to access all data 00047 * associate with a specific record. 00048 * 00049 * Finally, the property tables in Chado generally follow the same format. Therefore 00050 * there is a set of functions for inserting, updating and deleting properties for 00051 * any table. This provides quick lookup of properties (provided the CV term is 00052 * known). 00053 * 00054 * @} 00055 * @ingroup tripal_api 00056 */ 00057 00058 /** 00059 * @defgroup tripal_files_api Core Module Files API 00060 * @{ 00061 * Provides an application programming interface (API) for managing files within 00062 * the Tripal data directory structure. 00063 * 00064 * @} 00065 * @ingroup tripal_api 00066 */ 00067 00068 /** 00069 * Provides a generic routine for inserting into any Chado table 00070 * 00071 * Use this function to insert a record into any Chado table. The first 00072 * argument specifies the table for inserting and the second is an array 00073 * of values to be inserted. The array is mutli-dimensional such that 00074 * foreign key lookup values can be specified. 00075 * 00076 * @param $table 00077 * The name of the chado table for inserting 00078 * @param $values 00079 * An associative array containing the values for inserting. 00080 * 00081 * @return 00082 * On success this function returns TRUE. On failure, it returns FALSE. 00083 * 00084 * Example usage: 00085 * @code 00086 * $values = array( 00087 * 'organism_id' => array( 00088 * 'genus' => 'Citrus', 00089 * 'species' => 'sinensis', 00090 * ), 00091 * 'name' => 'orange1.1g000034m.g', 00092 * 'uniquename' => 'orange1.1g000034m.g', 00093 * 'type_id' => array ( 00094 * 'cv_id' => array ( 00095 * 'name' => 'sequence', 00096 * ), 00097 * 'name' => 'gene', 00098 * 'is_obsolete' => 0 00099 * ), 00100 * ); 00101 * $result = tripal_core_chado_insert('feature',$values); 00102 * @endcode 00103 * The above code inserts a record into the feature table. The $values array is 00104 * nested such that the organism is selected by way of the organism_id foreign 00105 * key constraint by specifying the genus and species. The cvterm is also 00106 * specified using its foreign key and the cv_id for the cvterm is nested as 00107 * well. 00108 * 00109 * @ingroup tripal_chado_api 00110 */ 00111 function tripal_core_chado_insert($table,$values){ 00112 $insert_values = array(); 00113 00114 // get the table description 00115 $table_desc = module_invoke_all('chado_'.$table.'_schema'); 00116 00117 // iterate through the values array and create a new 'insert_values' array 00118 // that has all the values needed for insert with all foreign relationsihps 00119 // resolved. 00120 foreach($values as $field => $value){ 00121 if(is_array($value)){ 00122 // select the value from the foreign key relationship for this value 00123 $results = tripal_core_chado_get_foreign_key($table_desc,$field,$value); 00124 if (sizeof($results) > 1) { 00125 watchdog('tripal_core', 'tripal_core_chado_insert: Too many records match the criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR); 00126 } elseif (sizeof($results) < 1) { 00127 watchdog('tripal_core', 'tripal_core_chado_insert: no record matches criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR); 00128 } else { 00129 $insert_values[$field] = $results[0]; 00130 } 00131 } 00132 else { 00133 $insert_values[$field] = $value; 00134 } 00135 } 00136 00137 // check for violation of any unique constraints 00138 $ukeys = $table_desc['unique keys']; 00139 $ukselect_cols = array(); 00140 $ukselect_vals = array(); 00141 if ($ukeys) { 00142 foreach($ukeys as $name => $fields){ 00143 foreach($fields as $index => $field){ 00144 // build the arrays for performing a select that will check the contraint 00145 array_push($ukselect_cols,$field); 00146 $ukselect_vals[$field] = $insert_values[$field]; 00147 } 00148 // now check the constraint 00149 if(tripal_core_chado_select($table,$ukselect_cols,$ukselect_vals)){ 00150 watchdog('tripal_core',"tripal_core_chado_insert: Cannot insert duplicate record into $table table: " . print_r($values,1),array(),'WATCHDOG_ERROR'); 00151 return false; 00152 } 00153 } 00154 } 00155 00156 // if trying to insert a field that is the primary key, make sure it also is unique 00157 $pkey = $table_desc['primary key'][0]; 00158 if($insert_values[$pkey]){ 00159 if(tripal_core_chado_select($table,array($pkey),array($pkey => $insert_values[$pkey]))){ 00160 watchdog('tripal_core',"tripal_core_chado_insert: Cannot insert duplicate primary key into $table table: " . print_r($values,1),array(),'WATCHDOG_ERROR'); 00161 return false; 00162 } 00163 } 00164 00165 // make sure required fields have a value 00166 $fields = $table_desc['fields']; 00167 foreach($fields as $field => $def){ 00168 // a field is considered missing if it cannot be null and there is no default 00169 // value for it or it is of type 'serial' 00170 if($def['not null'] == 1 and !array_key_exists($field,$insert_values) and !isset($def['default']) and strcmp($def['type'],serial)!=0){ 00171 watchdog('tripal_core',"tripal_core_chado_insert: Field $table.$field cannot be null: " . print_r($values,1),array(),'WATCHDOG_ERROR'); 00172 return false; 00173 } 00174 } 00175 00176 // Now build the insert SQL statement 00177 $ifields = array(); 00178 $ivalues = array(); 00179 $itypes = array(); 00180 foreach ($insert_values as $field => $value){ 00181 array_push($ifields,$field); 00182 array_push($ivalues,$value); 00183 if(strcmp($fields[$field]['type'],'serial')==0 or 00184 strcmp($fields[$field]['type'],'int')==0){ 00185 array_push($itypes,"%d"); 00186 } else { 00187 array_push($itypes,"'%s'"); 00188 } 00189 } 00190 $sql = "INSERT INTO {$table} (" . implode(", ",$ifields) . ") VALUES (". implode(", ",$itypes) .")"; 00191 00192 // finally perform the insert. 00193 $previous_db = tripal_db_set_active('chado'); // use chado database 00194 $result = db_query($sql,$ivalues); 00195 tripal_db_set_active($previous_db); // now use drupal database 00196 if($result){ 00197 // add primary keys to values before return 00198 $primary_key = array(); 00199 foreach ($table_desc['primary key'] as $field) { 00200 $value = db_last_insert_id($table, $field); 00201 $values[$field] = $value; 00202 } 00203 return $values; 00204 } 00205 else { 00206 watchdog('tripal_core',"tripal_core_chado_insert: Cannot insert record into $table table: " . print_r($values,1),array(),'WATCHDOG_ERROR'); 00207 return false; 00208 } 00209 return false; 00210 } 00211 00212 /** 00213 * Provides a generic function for deleting a record(s) from any chado table 00214 * 00215 * Use this function to delete a record(s) in any Chado table. The first 00216 * argument specifies the table to delete from and the second is an array 00217 * of values to match for locating the record(s) to be deleted. The arrays 00218 * are mutli-dimensional such that foreign key lookup values can be specified. 00219 * 00220 * @param $table 00221 * The name of the chado table for inserting 00222 * @param $match 00223 * An associative array containing the values for locating a record to update. 00224 * 00225 * @return 00226 * On success this function returns TRUE. On failure, it returns FALSE. 00227 * 00228 * Example usage: 00229 * @code 00230 $umatch = array( 00231 'organism_id' => array( 00232 'genus' => 'Citrus', 00233 'species' => 'sinensis', 00234 ), 00235 'uniquename' => 'orange1.1g000034m.g7', 00236 'type_id' => array ( 00237 'cv_id' => array ( 00238 'name' => 'sequence', 00239 ), 00240 'name' => 'gene', 00241 'is_obsolete' => 0 00242 ), 00243 ); 00244 $uvalues = array( 00245 'name' => 'orange1.1g000034m.g', 00246 'type_id' => array ( 00247 'cv_id' => array ( 00248 'name' => 'sequence', 00249 ), 00250 'name' => 'mRNA', 00251 'is_obsolete' => 0 00252 ), 00253 ); 00254 * $result = tripal_core_chado_update('feature',$umatch,$uvalues); 00255 * @endcode 00256 * The above code species that a feature with a given uniquename, organism_id, 00257 * and type_id (the unique constraint for the feature table) will be deleted. 00258 * The organism_id is specified as a nested array that uses the organism_id 00259 * foreign key constraint to lookup the specified values to find the exact 00260 * organism_id. The same nested struture is also used for specifying the 00261 * values to update. The function will find all records that match the 00262 * columns specified and delete them. 00263 * 00264 * @ingroup tripal_chado_api 00265 */ 00266 function tripal_core_chado_delete($table,$match){ 00267 $delete_matches = array(); // contains the values for the where clause 00268 00269 // get the table description 00270 $table_desc = module_invoke_all('chado_'.$table.'_schema'); 00271 00272 // get the values needed for matching in the SQL statement 00273 foreach ($match as $field => $value){ 00274 if(is_array($value)){ 00275 $results = tripal_core_chado_get_foreign_key($table_desc,$field,$value); 00276 if (sizeof($results) > 1) { 00277 watchdog('tripal_core', 'tripal_core_chado_delete: When trying to find record to delete, too many records match the criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR); 00278 } elseif (sizeof($results) < 1) { 00279 watchdog('tripal_core', 'tripal_core_chado_delete: When trying to find record to delete, no record matches criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR); 00280 } else { 00281 $delete_matches[$field] = $results[0]; 00282 } 00283 } 00284 else { 00285 $delete_matches[$field] = $value; 00286 } 00287 } 00288 // now build the SQL statement 00289 $sql = "DELETE FROM {$table} WHERE "; 00290 $dargs = array(); 00291 foreach($delete_matches as $field => $value){ 00292 if(strcmp($fields[$field]['type'],'serial')==0 or 00293 strcmp($fields[$field]['type'],'int')==0){ 00294 $sql .= " $field = %d AND "; 00295 } else { 00296 $sql .= " $field = '%s' AND "; 00297 } 00298 array_push($dargs,$value); 00299 } 00300 $sql = substr($sql,0,-4); // get rid of the trailing 'AND' 00301 00302 // finally perform the delete. If successful, return the updated record 00303 $previous_db = tripal_db_set_active('chado'); // use chado database 00304 $result = db_query($sql,$dargs); 00305 tripal_db_set_active($previous_db); // now use drupal database 00306 if($result){ 00307 return true; 00308 } 00309 else { 00310 watchdog('tripal_core',"Cannot delete record in $table table. Match:" . print_r($match,1) . ". Values: ". print_r($values,1),array(),'WATCHDOG_ERROR'); 00311 return false; 00312 } 00313 return false; 00314 } 00315 00316 /** 00317 * Provides a generic routine for updating into any Chado table 00318 * 00319 * Use this function to update a record in any Chado table. The first 00320 * argument specifies the table for inserting, the second is an array 00321 * of values to matched for locating the record for updating, and the third 00322 * argument give the values to update. The arrays are mutli-dimensional such 00323 * that foreign key lookup values can be specified. 00324 * 00325 * @param $table 00326 * The name of the chado table for inserting 00327 * @param $match 00328 * An associative array containing the values for locating a record to update. 00329 * @param $values 00330 * An associative array containing the values for updating. 00331 * 00332 * @return 00333 * On success this function returns TRUE. On failure, it returns FALSE. 00334 * 00335 * Example usage: 00336 * @code 00337 $umatch = array( 00338 'organism_id' => array( 00339 'genus' => 'Citrus', 00340 'species' => 'sinensis', 00341 ), 00342 'uniquename' => 'orange1.1g000034m.g7', 00343 'type_id' => array ( 00344 'cv_id' => array ( 00345 'name' => 'sequence', 00346 ), 00347 'name' => 'gene', 00348 'is_obsolete' => 0 00349 ), 00350 ); 00351 $uvalues = array( 00352 'name' => 'orange1.1g000034m.g', 00353 'type_id' => array ( 00354 'cv_id' => array ( 00355 'name' => 'sequence', 00356 ), 00357 'name' => 'mRNA', 00358 'is_obsolete' => 0 00359 ), 00360 ); 00361 * $result = tripal_core_chado_update('feature',$umatch,$uvalues); 00362 * @endcode 00363 * The above code species that a feature with a given uniquename, organism_id, 00364 * and type_id (the unique constraint for the feature table) will be updated. 00365 * The organism_id is specified as a nested array that uses the organism_id 00366 * foreign key constraint to lookup the specified values to find the exact 00367 * organism_id. The same nested struture is also used for specifying the 00368 * values to update. The function will find the record that matches the 00369 * columns specified and update the record with the avlues in the $uvalues array. 00370 * 00371 * @ingroup tripal_chado_api 00372 */ 00373 function tripal_core_chado_update($table,$match,$values){ 00374 $update_values = array(); // contains the values to be updated 00375 $update_matches = array(); // contains the values for the where clause 00376 00377 // get the table description 00378 $table_desc = module_invoke_all('chado_'.$table.'_schema'); 00379 00380 // get the values needed for matching in the SQL statement 00381 foreach ($match as $field => $value){ 00382 if(is_array($value)){ 00383 $results = tripal_core_chado_get_foreign_key($table_desc,$field,$value); 00384 if (sizeof($results) > 1) { 00385 watchdog('tripal_core', 'tripal_core_chado_update: When trying to find record to update, too many records match the criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR); 00386 } elseif (sizeof($results) < 1) { 00387 watchdog('tripal_core', 'tripal_core_chado_update: When trying to find record to update, no record matches criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR); 00388 } else { 00389 $update_matches[$field] = $results[0]; 00390 } 00391 } 00392 else { 00393 $update_matches[$field] = $value; 00394 } 00395 } 00396 00397 // get the values used for updating 00398 foreach ($values as $field => $value){ 00399 if(is_array($value)){ 00400 $results = tripal_core_chado_get_foreign_key($table_desc,$field,$value); 00401 if (sizeof($results) > 1) { 00402 watchdog('tripal_core', 'tripal_core_chado_update: When trying to find update values, too many records match the criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR); 00403 } elseif (sizeof($results) < 1) { 00404 watchdog('tripal_core', 'tripal_core_chado_update: When trying to find update values, no record matches criteria supplied for !foreign_key foreign key constraint (!criteria)', array('!foreign_key' => $field, '!criteria' => print_r($value,TRUE)), WATCHDOG_ERROR); 00405 } else { 00406 $update_values[$field] = $results[0]; 00407 } 00408 } 00409 else { 00410 $update_values[$field] = $value; 00411 } 00412 } 00413 00414 // now build the SQL statement 00415 $sql = "UPDATE {$table} SET "; 00416 $fields = $table_desc['fields']; 00417 $uargs = array(); 00418 foreach($update_values as $field => $value){ 00419 if(strcmp($fields[$field]['type'],'serial')==0 or 00420 strcmp($fields[$field]['type'],'int')==0){ 00421 $sql .= " $field = %d, "; 00422 } else { 00423 $sql .= " $field = '%s', "; 00424 } 00425 array_push($uargs,$value); 00426 } 00427 $sql = substr($sql,0,-2); // get rid of the trailing comma & space 00428 $sql .= " WHERE "; 00429 foreach($update_matches as $field => $value){ 00430 if(strcmp($fields[$field]['type'],'serial')==0 or 00431 strcmp($fields[$field]['type'],'int')==0){ 00432 $sql .= " $field = %d AND "; 00433 } else { 00434 $sql .= " $field = '%s' AND "; 00435 } 00436 array_push($uargs,$value); 00437 } 00438 $sql = substr($sql,0,-4); // get rid of the trailing 'AND' 00439 00440 // finally perform the update. If successful, return the updated record 00441 $previous_db = tripal_db_set_active('chado'); // use chado database 00442 $result = db_query($sql,$uargs); 00443 tripal_db_set_active($previous_db); // now use drupal database 00444 if($result){ 00445 return true; 00446 } 00447 else { 00448 watchdog('tripal_core',"Cannot update record in $table table. Match:" . print_r($match,1) . ". Values: ". print_r($values,1),array(),'WATCHDOG_ERROR'); 00449 return false; 00450 } 00451 return false; 00452 } 00453 00454 /** 00455 * Provides a generic routine for selecting data from a Chado table 00456 * 00457 * Use this function to perform a simple select from any Chado table. 00458 * 00459 * @param $table 00460 * The name of the chado table for inserting 00461 * @param $columns 00462 * An array of column names 00463 * @param $values 00464 * An associative array containing the values for filtering the results. In the 00465 * case where multiple values for the same time are to be selected an additional 00466 * entry for the field should appear for each value 00467 * @param $options 00468 * An associative array of additional options where the key is the option 00469 * and the value is the value of that option. 00470 * 00471 * Additional Options Include: 00472 * - has_record 00473 * Set this argument to 'true' to have this function return a numeric 00474 * value for the number of recrods rather than the array of records. this 00475 * can be useful in 'if' statements to check the presence of particula records. 00476 * - return_sql 00477 * Set this to 'true' to have this function return an array where the first 00478 * element is the sql that would have been run and the second is an array of 00479 * arguments. 00480 * - case_insensitive_columns 00481 * An array of columns to do a case insensitive search on. 00482 * - regex_columns 00483 * An array of columns where the value passed in should be treated as a regular expression 00484 * - order_by 00485 * An associative array containing the column names of the table as keys 00486 * and the type of sort (i.e. ASC, DESC) as the values. The results in the 00487 * query will be sorted by the key values in the direction listed by the value 00488 * 00489 * @return 00490 * A database query result resource, FALSE if the query was not executed 00491 * correctly, or the number of records in the dataset if $has_record is set. 00492 * 00493 * Example usage: 00494 * @code 00495 * $columns = array('feature_id','name'); 00496 * $values = array( 00497 * 'organism_id' => array( 00498 * 'genus' => 'Citrus', 00499 * 'species' => array('sinensis','clementina'), 00500 * ), 00501 * 'uniquename' => 'orange1.1g000034m.g', 00502 * 'type_id' => array ( 00503 * 'cv_id' => array ( 00504 * 'name' => 'sequence', 00505 * ), 00506 * 'name' => 'gene', 00507 * 'is_obsolete' => 0 00508 * ), 00509 * ); 00510 * $result = tripal_core_chado_select('feature',$columns,$values); 00511 * @endcode 00512 * The above code selects a record from the feature table using the three fields 00513 * that uniquely identify a feature. The $columns array simply lists the columns 00514 * to select. The $values array is nested such that the organism is identified by 00515 * way of the organism_id foreign key constraint by specifying the genus and 00516 * species. The cvterm is also specified using its foreign key and the cv_id 00517 * for the cvterm is nested as well. In the example above, two different species 00518 * are allowed to match 00519 * 00520 * @ingroup tripal_chado_api 00521 */ 00522 function tripal_core_chado_select($table,$columns,$values,$options = null){ 00523 if (!is_array($options)) { $options = array(); } 00524 if (!$options['case_insensitive_columns']) { $options['case_insensitive_columns'] = array(); } 00525 if (!$options['regex_columns']) { $options['regex_columns'] = array(); } 00526 if (!$options['order_by']) { $options['order_by'] = array(); } 00527 00528 if (!is_array($columns)){ 00529 watchdog('tripal_core', 'the $columns argument for tripal_core_chado_select must be an array.'); 00530 return false; 00531 } 00532 00533 if (!is_array($values)){ 00534 watchdog('tripal_core', 'the $values argument for tripal_core_chado_select must be an array.'); 00535 return false; 00536 } 00537 00538 // get the table description 00539 $table_desc = module_invoke_all('chado_'.$table.'_schema'); 00540 00541 $select = ''; 00542 $from = ''; 00543 $where = ''; 00544 $args = array(); 00545 foreach($values as $field => $value){ 00546 $select[] = $field; 00547 if(is_array($value)){ 00548 // if the user has specified multiple values for matching then this we 00549 // want to catch that and save them in our $where array, otherwise 00550 // we'll descend for a foreign key relationship 00551 if(array_values($value) === $value){ 00552 $where[$field] = $value; 00553 } else { 00554 // select the value from the foreign key relationship for this value 00555 $foreign_options = array( 00556 'regex_columns' => $options['regex_columns'], 00557 'case_insensitive_columns' => $options['case_insensitive_columns'] 00558 ); 00559 $results = tripal_core_chado_get_foreign_key($table_desc,$field,$value, $foreign_options); 00560 if (sizeof($results) < 1) { 00561 // foreign key records are required 00562 // thus if none matched then return false and alert the admin through watchdog 00563 watchdog('tripal_core', 00564 'tripal_core_chado_select: no record in the table referenced by the foreign key (!field) exists. tripal_core_chado_select table=!table, columns=!columns, values=!values', 00565 array('!table' => $table, 00566 '!columns' => '<pre>' . print_r($columns, TRUE) . '</pre>', 00567 '!values' => '<pre>' . print_r($values, TRUE) . '</pre>', 00568 '!field' => $field, 00569 ), 00570 WATCHDOG_WARNING); 00571 return false; 00572 } else { 00573 $where[$field] = $results; 00574 } 00575 } 00576 } 00577 else { 00578 //need to catch a 0 and make int if integer field 00579 if ($table_desc['fields'][$field]['type'] == 'int') { 00580 $where[$field][] = (int) $value; 00581 } else { 00582 $where[$field][] = $value; 00583 } 00584 } 00585 } 00586 00587 // now build the SQL select statement 00588 if (empty($where)) { 00589 // sometimes want to select everything 00590 $sql = "SELECT " . implode(',',$columns) . " "; 00591 $sql .= "FROM {$table} "; 00592 } else { 00593 $sql = "SELECT " . implode(',',$columns) . " "; 00594 $sql .= "FROM {$table} "; 00595 $sql .= "WHERE "; 00596 foreach($where as $field => $value){ 00597 if (count($value) > 1) { 00598 $sql .= "$field IN (".db_placeholders($value,'varchar').") AND "; 00599 foreach ($value as $v) { $args[] = $v; } 00600 } else { 00601 $operator = '='; 00602 if (in_array($field, $options['regex_columns'])) { 00603 $operator = '~*'; 00604 } 00605 if (in_array($field, $options['case_insensitive_columns'])) { 00606 $sql .= "lower($field) $operator lower('%s') AND "; 00607 $args[] = $value[0]; 00608 } else { 00609 $sql .= "$field $operator '%s' AND "; 00610 $args[] = $value[0]; 00611 } 00612 } 00613 } 00614 $sql = substr($sql,0,-4); // get rid of the trailing 'AND ' 00615 } 00616 // finally add any ordering of the results to the SQL statement 00617 if(count($options['order_by']) > 0){ 00618 $sql .= " ORDER BY "; 00619 foreach($options['order_by'] as $field => $dir){ 00620 $sql .= "$field $dir, "; 00621 } 00622 $sql = substr($sql,0,-2); // get rid of the trailing ', ' 00623 } 00624 00625 00626 // if the caller has requested the SQL rather than the results... 00627 // which happens in the case of wanting to use the Drupal pager, then do so 00628 if($options['return_sql']){ 00629 return array('sql'=> $sql, 'args' => $args); 00630 } 00631 00632 $previous_db = tripal_db_set_active('chado'); // use chado database 00633 $resource = db_query($sql,$args); 00634 tripal_db_set_active($previous_db); // now use drupal database 00635 00636 $results = array(); 00637 while ($r = db_fetch_object($resource)) { 00638 $results[] = $r; 00639 } 00640 00641 if(!$options['has_record']){ 00642 return $results; 00643 } else{ 00644 return count($results); 00645 } 00646 } 00647 00648 /** 00649 * Gets the value of a foreign key relationship 00650 * 00651 * This function is used by tripal_core_chado_select, tripal_core_chado_insert, 00652 * and tripal_core_chado_update to iterate through the associate array of 00653 * values that gets passed to each of those routines. The values array 00654 * is nested where foreign key contraints are used to specify a value that. See 00655 * documentation for any of those functions for further information. 00656 * 00657 * @param $table_desc 00658 * A table description for the table with the foreign key relationship to be identified generated by 00659 * hook_chado_<table name>_schema() 00660 * @param $field 00661 * The field in the table that is the foreign key. 00662 * @param $values 00663 * An associative array containing the values 00664 * @param $options 00665 * An associative array of additional options where the key is the option 00666 * and the value is the value of that option. These options are passed on to tripal_core_chado_select. 00667 * 00668 * Additional Options Include: 00669 * - case_insensitive_columns 00670 * An array of columns to do a case insensitive search on. 00671 * - regex_columns 00672 * An array of columns where the value passed in should be treated as a regular expression 00673 * 00674 * @return 00675 * A string containg the results of the foreign key lookup, or FALSE if failed. 00676 * 00677 * Example usage: 00678 * @code 00679 * 00680 * $values = array( 00681 * 'genus' => 'Citrus', 00682 * 'species' => 'sinensis', 00683 * ); 00684 * $value = tripal_core_chado_get_foreign_key('feature','organism_id',$values); 00685 * 00686 * @endcode 00687 * The above code selects a record from the feature table using the three fields 00688 * that uniquely identify a feature. The $columns array simply lists the columns 00689 * to select. The $values array is nested such that the organism is identified by 00690 * way of the organism_id foreign key constraint by specifying the genus and 00691 * species. The cvterm is also specified using its foreign key and the cv_id 00692 * for the cvterm is nested as well. 00693 * 00694 * @ingroup tripal_chado_api 00695 */ 00696 function tripal_core_chado_get_foreign_key($table_desc,$field,$values, $options = null){ 00697 if (!is_array($options)) { $options = array(); } 00698 if (!$options['case_insensitive_columns']) { $options['case_insensitive_columns'] = array(); } 00699 if (!$options['regex_columns']) { $options['regex_columns'] = array(); } 00700 00701 // get the list of foreign keys for this table description and 00702 // iterate through those until we find the one we're looking for 00703 $fkeys = $table_desc['foreign keys']; 00704 if($fkeys){ 00705 foreach($fkeys as $name => $def){ 00706 if (is_array($def['table'])) { 00707 //foreign key was described 2X 00708 $message = "The foreign key ".$name." was defined twice. Please check modules to determine if hook_chado_".$table_desc['table']."_schema() was implemented and defined this foreign key when it wasn't supposed to. Modules this hook was implemented in: ".implode(', ', module_implements("chado_".$table_desc['table']."_schema"))."."; 00709 watchdog('tripal_core', $message); 00710 drupal_set_message($message,'error'); 00711 continue; 00712 } 00713 $table = $def['table']; 00714 $columns = $def['columns']; 00715 // iterate through the columns of the foreign key relationship 00716 foreach($columns as $left => $right){ 00717 // does the left column in the relationship match our field? 00718 if(strcmp($field,$left)==0){ 00719 // the column name of the foreign key matches the field we want 00720 // so this is the right relationship. Now we want to select 00721 $select_cols = array($right); 00722 $result = tripal_core_chado_select($table,$select_cols,$values, $options); 00723 $fields = array(); 00724 foreach ($result as $obj) { 00725 $fields[] = $obj->$right; 00726 } 00727 return $fields; 00728 } 00729 } 00730 } 00731 } 00732 else { 00733 // TODO: what do we do if we get to this point and we have a fk 00734 // relationship expected but we don't have any definition for one in the 00735 // table schema?? 00736 $message = "There is no foreign key relationship defined for ".$field.". 00737 To define a foreign key relationship, determine the table this foreign 00738 key referrs to (<foreign table>) and then implement 00739 hook_chado_<foreign table>_schema(). See 00740 tripal_feature_chado_feature_schema for an example."; 00741 watchdog('tripal_core', $message); 00742 drupal_set_message($message,'error'); 00743 } 00744 return false; 00745 } 00746 00747 /** 00748 * Generates an object containing the full details of a record(s) in chado. 00749 * 00750 * This differs from the objects returned by tripal_core_chado_select in so far as all foreign key 00751 * relationships have been followed meaning you have more complete details. Thus this function 00752 * should be used whenever you need a full variable and tripal_core_chado_select should be used if 00753 * you only case about a few columns. 00754 * 00755 * @param $table 00756 * The name of the base table to generate a variable for 00757 * @param $values 00758 * A select values array that selects the records you want from the base table 00759 * (this has the same form as tripal_core_chado_select) 00760 * @param $base_options 00761 * An array containing options for the base table. For example, an 00762 * option of 'order_by' may be used to sort results in the base table 00763 * if more than one are returned. The options must be compatible with 00764 * the options accepted by the tripal_core_chado_select() function. 00765 * @return 00766 * Either an object (if only one record was selected from the base table) 00767 * or an array of objects (if more than one record was selected from the base table). 00768 * 00769 * Example Usage: 00770 * @code 00771 $values = array( 00772 'name' => 'Medtr4g030710' 00773 ); 00774 $features = tripal_core_generate_chado_var('feature', $values); 00775 * @endcode 00776 * This will return an object if there is only one feature with the name Medtr4g030710 or it will 00777 * return an array of feature objects if more than one feature has that name. 00778 * 00779 * Note to Module Designers: Fields can be excluded by default from these objects by implementing 00780 * one of the following hooks: 00781 * - hook_exclude_field_from_tablename_by_default (where tablename is the name of the table): 00782 * This hook allows you to add fields to be excluded on a per table basis. Simply implement 00783 * this hook to return an array of fields to be excluded. For example: 00784 * @code 00785 mymodule_exclude_field_from_feature_by_default() { 00786 return array('residues' => TRUE); 00787 } 00788 * @endcode 00789 * will ensure that feature.residues is ecluded from a feature object by default. 00790 * - hook_exclude_type_by_default: 00791 * This hook allows you to exclude fields from all tables that are of a given postgresql field 00792 * type. Simply implement this hook to return an array of postgresql types mapped to criteria. 00793 * Then all fields of that type where the criteria supplied returns TRUE will be excluded from 00794 * any table. Tokens available in criteria are >field_value< and >field_name< . For example: 00795 * @code 00796 mymodule_exclude_type_by_default() { 00797 return array('text' => 'length(>field_value< ) > 50'); 00798 } 00799 * @endcode 00800 * will exclude all text fields with a length > 50. Thus if $feature.residues is longer than 50 * it will be excluded, otherwise it will be added. 00801 * 00802 * @ingroup tripal_chado_api 00803 */ 00804 function tripal_core_generate_chado_var($table, $values, $base_options=array()) { 00805 00806 // get description for the current table---------------------------------------------------------- 00807 $table_desc = module_invoke_all('chado_'.$table.'_schema'); 00808 $table_primary_key = $table_desc['primary key'][0]; 00809 $table_columns = array_keys($table_desc['fields']); 00810 00811 // Expandable fields without value needed for criteria-------------------------------------------- 00812 $all->expandable_fields = array(); 00813 if ($table_desc['referring_tables']) { 00814 $all->expandable_tables = $table_desc['referring_tables']; 00815 } else { 00816 $all->expandable_tables = array(); 00817 } 00818 $all->expandable_nodes = array(); 00819 00820 // Get fields to be removed by name................................. 00821 $fields_to_remove = module_invoke_all('exclude_field_from_'.$table.'_by_default'); 00822 foreach ($fields_to_remove as $field_name => $criteria) { 00823 //replace >field_name< with the current field name & 00824 $criteria = preg_replace('/>field_name< /', $field_name, $criteria); 00825 00826 // if field_value needed we can't deal with this field yet 00827 if (preg_match('/>field_value< /', $criteria)) { break; } 00828 00829 //if criteria then remove from query 00830 $success = drupal_eval('<?php return '.$criteria.'; ?>'); 00831 // watchdog('tripal_core', 00832 // 'Evaluating criteria (%criteria) for field %field in tripal_core_generate_chado_var for %table evaluated to %success', 00833 // array('%table' => $table, '%criteria'=>$criteria, '%field' => $field_name, '%success'=>$success), 00834 // WATCHDOG_NOTICE 00835 // ); 00836 if ($success) { 00837 unset($table_columns[array_search($field_name, $table_columns)]); 00838 unset($fields_to_remove[$field_name]); 00839 $all->expandable_fields[] = $table . '.' . $field_name; 00840 } 00841 } 00842 00843 //Get fields to be removed by type................................ 00844 $types_to_remove = module_invoke_all('exclude_type_by_default'); 00845 $field_types = array(); 00846 foreach ($table_desc['fields'] as $field_name => $field_array) { 00847 $field_types[$field_array['type']][] = $field_name; 00848 } 00849 foreach ($types_to_remove as $field_type => $criteria) { 00850 // if there are fields of that type to remove 00851 if (is_array($field_types[$field_type])) { 00852 //replace >field_name< with the current field name & 00853 $criteria = preg_replace('/>field_name< /', $field_name, $criteria); 00854 00855 foreach ($field_types[$field_type] as $field_name) { 00856 // if field_value needed we can't deal with this field yet 00857 if (preg_match('/>field_value< /', $criteria)) { 00858 $fields_to_remove[$field_name] = $criteria; 00859 continue; 00860 } 00861 00862 // if field_value needed we can't deal with this field yet 00863 if (preg_match('/>field_value< /', $criteria)) { break; } 00864 00865 //if criteria then remove from query 00866 $success = drupal_eval('<?php return '.$criteria.'; ?>'); 00867 // watchdog('tripal_core', 00868 // 'Evaluating criteria (%criteria) for field %field of $type in tripal_core_generate_chado_var for %table evaluated to %success', 00869 // array('%table'=>$table, '%criteria'=>$criteria, '%field'=>$field_name, '%type'=>$field_type, '%success'=>$success), 00870 // WATCHDOG_NOTICE 00871 // ); 00872 if ($success) { 00873 unset($table_columns[array_search($field_name, $table_columns)]); 00874 $all->expandable_fields[] = $table . '.' . $field_name; 00875 } 00876 } //end of foreach field of that type 00877 } 00878 } //end of foreach type to be removed 00879 00880 // get the values for the record in the current table--------------------------------------------- 00881 $results = tripal_core_chado_select($table, $table_columns, $values,$base_options); 00882 00883 if($results){ 00884 foreach ($results as $key => $object) { 00885 // Add empty expandable_x arrays 00886 $object->expandable_fields = $all->expandable_fields; 00887 $object->expandable_tables = $all->expandable_tables; 00888 $object->expandable_nodes = $all->expandable_nodes; 00889 00890 // add curent table 00891 $object->tablename = $table; 00892 00893 // check if the current table maps to a node type----------------------------------------------- 00894 // if this table is connected to a node there will be a chado_tablename table in drupal 00895 if (db_table_exists('chado_'.$table)) { 00896 // that has a foreign key to this one ($table_desc['primary key'][0] 00897 // and to the node table (nid) 00898 $sql = "SELECT %s, nid FROM chado_%s WHERE %s=%d"; 00899 $mapping = db_fetch_object(db_query( 00900 $sql, 00901 $table_primary_key, 00902 $table, 00903 $table_primary_key, 00904 $object->{$table_primary_key} 00905 )); 00906 if ($mapping->{$table_primary_key}) { 00907 $object->nid = $mapping->nid; 00908 $object->expandable_nodes[] = $table; 00909 } 00910 } 00911 00912 // remove any fields where criteria need to be evalulated--------------------------------------- 00913 foreach ($fields_to_remove as $field_name => $criteria) { 00914 if (!isset($object->{$field_name})) { break; } 00915 $criteria = preg_replace('/>field_value< /', $object->{$field_name}, $criteria); 00916 //if criteria then remove from query 00917 $success = drupal_eval('<?php return '.$criteria.'; ?>'); 00918 // watchdog('tripal_core', 00919 // 'Evaluating criteria (%criteria) for field %field in tripal_core_generate_chado_var for %table evaluated to %success', 00920 // array('%table' => $table, '%criteria'=>$criteria, '%field' => $field_name, '%success'=>$success), 00921 // WATCHDOG_NOTICE 00922 // ); 00923 if ($success) { 00924 unset($object->{$field_name}); 00925 $object->expandable_fields[] = $table . '.' . $field_name; 00926 } 00927 } 00928 00929 // recursively follow foreign key relationships nesting objects as we go------------------------ 00930 if ($table_desc['foreign keys']) { 00931 foreach ($table_desc['foreign keys'] as $foreign_key_array) { 00932 $foreign_table = $foreign_key_array['table']; 00933 foreach ($foreign_key_array['columns'] as $foreign_key => $primary_key) { 00934 // Note: Foreign key is the field in the current table whereas primary_key is the field in 00935 // the table referenced by the foreign key 00936 00937 //Dont do anything if the foreign key is empty 00938 if (empty($object->{$foreign_key})) { 00939 break; 00940 } 00941 00942 // get the record from the foreign table 00943 $foreign_values = array($primary_key => $object->{$foreign_key}); 00944 $foreign_object = tripal_core_generate_chado_var($foreign_table, $foreign_values); 00945 00946 // add the foreign record to the current object in a nested manner 00947 $object->{$foreign_key} = $foreign_object; 00948 00949 // Flatten expandable_x arrays so only in the bottom object 00950 if (is_array($object->{$foreign_key}->expandable_fields)) { 00951 $object->expandable_fields = array_merge( 00952 $object->expandable_fields, 00953 $object->{$foreign_key}->expandable_fields 00954 ); 00955 unset($object->{$foreign_key}->expandable_fields); 00956 } 00957 if (is_array($object->{$foreign_key}->expandable_tables)) { 00958 $object->expandable_tables = array_merge( 00959 $object->expandable_tables, 00960 $object->{$foreign_key}->expandable_tables 00961 ); 00962 unset($object->{$foreign_key}->expandable_tables); 00963 } 00964 if (is_array($object->{$foreign_key}->expandable_nodes)) { 00965 $object->expandable_nodes = array_merge( 00966 $object->expandable_nodes, 00967 $object->{$foreign_key}->expandable_nodes 00968 ); 00969 unset($object->{$foreign_key}->expandable_nodes); 00970 } 00971 } 00972 } 00973 00974 $results[$key] = $object; 00975 } 00976 } 00977 } 00978 00979 // check only one result returned 00980 if (sizeof($results) == 1) { 00981 // add results to object 00982 return $results[0]; 00983 } elseif (!empty($results)) { 00984 return $results; 00985 } else { 00986 // no results returned 00987 } 00988 00989 } 00990 00991 /** 00992 * Retrieves fields/tables/nodes that were excluded by default from a variable and adds them 00993 * 00994 * This function exists to allow tripal_core_generate_chado_var() to excldue some 00995 * fields/tables/nodes from the default form of a variable without making it extremely difficult for 00996 * the tripal admin to get at these variables if he/she wants them. 00997 * 00998 * @param $object 00999 * This must be an object generated using tripal_core_generate_chado_var() 01000 * @param $type 01001 * Must be one of 'field', 'table', 'node'. Indicates what is being expanded. 01002 * @param $to_expand 01003 * The name of the field/table/node to be expanded 01004 * @param $table_options 01005 * An array containing options for the base table. For example, an 01006 * option of 'order_by' may be used to sort results in the base table 01007 * if more than one are returned. The options must be compatible with 01008 * the options accepted by the tripal_core_chado_select() function. 01009 * @return 01010 * A chado object supplemented with the field/table/node requested to be expanded 01011 * 01012 * Example Usage: 01013 * @code 01014 // Get a chado object to be expanded 01015 $values = array( 01016 'name' => 'Medtr4g030710' 01017 ); 01018 $features = tripal_core_generate_chado_var('feature', $values); 01019 01020 // Expand the organism node 01021 $feature = tripal_core_expand_chado_vars($feature, 'node', 'organism'); 01022 01023 // Expand the feature.residues field 01024 $feature = tripal_core_expand_chado_vars($feature, 'field', 'feature.residues'); 01025 01026 // Expand the feature properties (featureprop table) 01027 $feature = tripal_core_expand_chado_vars($feature, 'table', 'featureprop'); 01028 * @endcode 01029 * 01030 * @ingroup tripal_chado_api 01031 */ 01032 function tripal_core_expand_chado_vars ($object, $type, $to_expand,$table_options = array()) { 01033 $base_table = $object->tablename; 01034 01035 // check to see if they are expanding an array of objects 01036 if (is_array($object)) { 01037 foreach ($object as $index => $o) { 01038 $object[$index] = tripal_core_expand_chado_vars($o,$type,$to_expand); 01039 } 01040 return $object; 01041 } 01042 01043 01044 switch ($type) { 01045 case "field": //-------------------------------------------------------------------------------- 01046 if (preg_match('/(\w+)\.(\w+)/', $to_expand, $matches)) { 01047 $tablename = $matches[1]; 01048 $fieldname = $matches[2]; 01049 $table_desc = module_invoke_all('chado_'.$tablename.'_schema'); 01050 01051 $values = array(); 01052 foreach($table_desc['primary key'] as $key) { 01053 $values[$key] = $object->{$key}; 01054 } 01055 01056 if ($base_table == $tablename) { 01057 //get the field 01058 $results = tripal_core_chado_select( 01059 $tablename, 01060 array($fieldname), 01061 $values 01062 ); 01063 $object->{$fieldname} = $results[0]->{$fieldname}; 01064 $object->expanded = $to_expand; 01065 } else { 01066 //We need to recurse -the field is in a nested object 01067 foreach ((array) $object as $field_name => $field_value) { 01068 if (is_object($field_value)) { 01069 $object->{$field_name} = tripal_core_expand_chado_vars( 01070 $field_value, 01071 'field', 01072 $to_expand 01073 ); 01074 } 01075 } //end of for each field in the current object 01076 } 01077 } else { 01078 watchdog( 01079 'tripal_core', 01080 'tripal_core_expand_chado_vars: Field (%field) not in the right format. It should be <tablename>.<fieldname>', 01081 WATCHDOG_ERROR 01082 ); 01083 } 01084 01085 break; 01086 case "table": //-------------------------------------------------------------------------------- 01087 $foreign_table = $to_expand; 01088 $foreign_table_desc = module_invoke_all('chado_'.$foreign_table.'_schema'); 01089 01090 // If it's connected to the base table 01091 if ($foreign_table_desc['foreign keys'][$base_table]) { 01092 foreach ($foreign_table_desc['foreign keys'][$base_table]['columns'] as $left => $right) { 01093 if (!$object->{$right}) { break; } 01094 01095 if (is_array($values)) { 01096 $values = array_merge($values, array($left => $object->{$right}) ); 01097 } else { 01098 $values = array($left => $object->{$right}); 01099 } 01100 $foreign_object = tripal_core_generate_chado_var( 01101 $foreign_table, 01102 array($left => $object->{$right}), 01103 $table_options 01104 ); 01105 01106 if ($foreign_object) { 01107 // in the case where the a foreign key relationships exists more 01108 // than once with the same table we want to alter the 01109 // array structure 01110 if(count($foreign_table_desc['foreign keys'][$base_table]['columns']) > 1){ 01111 $object->{$foreign_table}->{$left} = $foreign_object; 01112 $object->expanded = $to_expand; 01113 } else { 01114 $object->{$foreign_table} = $foreign_object; 01115 $object->expanded = $to_expand; 01116 } 01117 } 01118 } 01119 } else { 01120 //We need to recurse -the table has a relationship to one of the nested objects 01121 foreach ((array) $object as $field_name => $field_value) { 01122 // if we have a nested object ->expand the table in it 01123 if (is_object($field_value)) { 01124 $object->{$field_name} = tripal_core_expand_chado_vars( 01125 $field_value, 01126 'table', 01127 $foreign_table 01128 ); 01129 } 01130 } 01131 01132 } 01133 01134 break; 01135 case "node": //--------------------------------------------------------------------------------- 01136 //if the node to be expanded is for our base table, then just expand it 01137 if ($object->tablename == $to_expand) { 01138 $node = node_load($object->nid); 01139 if ($node) { 01140 $object->expanded = $to_expand; 01141 $node->expandable_fields = $object->expandable_fields; 01142 unset($object->expandable_fields); 01143 $node->expandable_tables = $object->expandable_tables; 01144 unset($object->expandable_tables); 01145 $node->expandable_nodes = $object->expandable_nodes; 01146 unset($object->expandable_nodes); 01147 $node->{$base_table} = $object; 01148 $object = $node; 01149 } else { 01150 watchdog( 01151 'tripal_core', 01152 'tripal_core_expand_chado_vars: No node matches the nid (%nid) supplied.', 01153 array('%nid'=>$object->nid), 01154 WATCHDOG_ERROR 01155 ); 01156 } //end of if node 01157 } else { 01158 //We need to recurse -the node to expand is one of the nested objects 01159 foreach ((array) $object as $field_name => $field_value) { 01160 if (is_object($field_value)) { 01161 $object->{$field_name} = tripal_core_expand_chado_vars( 01162 $field_value, 01163 'node', 01164 $to_expand 01165 ); 01166 } 01167 } //end of for each field in the current object 01168 } 01169 01170 break; 01171 default: 01172 watchdog('tripal_core', 01173 'tripal_core_expand_chado_vars: Unrecognized type (%type). Should be one of "field", "table", "node".', 01174 array('%type'=>$type), 01175 WATCHDOG_ERROR 01176 ); 01177 return FALSE; 01178 } 01179 01180 //move extended array downwards------------------------------------------------------------------- 01181 if (!$object->expanded) { 01182 //if there's no extended field then go hunting for it 01183 foreach ( (array)$object as $field_name => $field_value) { 01184 if (is_object($field_value)) { 01185 if (isset($field_value->expanded)) { 01186 $object->expanded = $field_value->expanded; 01187 unset($field_value->expanded); 01188 } 01189 } 01190 } 01191 } 01192 //try again becasue now we might have moved it down 01193 if ($object->expanded) { 01194 $expandable_name = 'expandable_'.$type.'s'; 01195 if ($object->{$expandable_name}) { 01196 $key_to_remove = array_search($object->expanded, $object->{$expandable_name}); 01197 unset($object->{$expandable_name}[$key_to_remove]); 01198 unset($object->expanded); 01199 } else { 01200 // if there is an expandable array then we've reached the base object 01201 // if we get here and don't have anything expanded then something went wrong 01202 // watchdog( 01203 // 'tripal_core', 01204 // 'tripal_core_expand_chado_vars: Unable to expand the %type %to_expand', 01205 // array('%type'=>$type, '%to_expand'=>$to_expand), 01206 // WATCHDOG_ERROR 01207 // ); 01208 } //end of it we've reached the base object 01209 } 01210 01211 return $object; 01212 } 01213 01214 /** 01215 * Implements hook_exclude_type_by_default() 01216 * 01217 * This hooks allows fields of a specified type that match a specified criteria to be excluded by 01218 * default from any table when tripal_core_generate_chado_var() is called. Keep in mind that if 01219 * fields are excluded by default they can always be expanded at a later date using 01220 * tripal_core_expand_chado_vars(). 01221 * 01222 * Criteria are php strings that evaluate to either TRUE or FALSE. These strings are evaluated using 01223 * drupal_eval() which suppresses syntax errors and throws watchdog entries of type php. There are 01224 * also watchdog entries of type tripal_core stating the exact criteria evaluated. Criteria can 01225 * contain the following tokens: 01226 * - >field_name< 01227 * Replaced by the name of the field to be excluded 01228 * - >field_value< 01229 * Replaced by the value of the field in the current record 01230 * Also keep in mind that if your criteria doesn't contain the >field_value< token then it will be 01231 * evaluated before the query is executed and if the field is excluded it won't be included in the 01232 * query. 01233 * 01234 * @return 01235 * An array of type => criteria where the type is excluded if the criteria evaluates to TRUE 01236 * 01237 * @ingroup tripal_chado_api 01238 */ 01239 function tripal_core_exclude_type_by_default() { 01240 return array('text' => "strlen('>field_value< ') > 100"); 01241 } 01242 01243 /** 01244 * Implements hook_exclude_field_from_<tablename>_by_default() 01245 * 01246 * This hooks allows fields from a specified table that match a specified criteria to be excluded by 01247 * default from any table when tripal_core_generate_chado_var() is called. Keep in mind that if 01248 * fields are excluded by default they can always be expanded at a later date using 01249 * tripal_core_expand_chado_vars(). 01250 * 01251 * Criteria are php strings that evaluate to either TRUE or FALSE. These strings are evaluated using 01252 * drupal_eval() which suppresses syntax errors and throws watchdog entries of type php. There are 01253 * also watchdog entries of type tripal_core stating the exact criteria evaluated. Criteria can 01254 * contain the following tokens: 01255 * - >field_name< 01256 * Replaced by the name of the field to be excluded 01257 * - >field_value< 01258 * Replaced by the value of the field in the current record 01259 * Also keep in mind that if your criteria doesn't contain the >field_value< token then it will be 01260 * evaluated before the query is executed and if the field is excluded it won't be included in the 01261 * query. 01262 * 01263 * @return 01264 * An array of type => criteria where the type is excluded if the criteria evaluates to TRUE 01265 * 01266 * @ingroup tripal_chado_api 01267 */ 01268 function tripal_core_exclude_field_from_feature_by_default() { 01269 return array(); 01270 } 01271 01272 /** 01273 * Use this function instead of db_query() to avoid switching databases 01274 * when making query to the chado database 01275 */ 01276 function chado_query($sql) { 01277 $args = func_get_args(); 01278 array_shift($args); 01279 $sql = db_prefix_tables($sql); 01280 if (isset($args[0]) and is_array($args[0])) { // 'All arguments in one array' syntax 01281 $args = $args[0]; 01282 } 01283 _db_query_callback($args, TRUE); 01284 $sql = preg_replace_callback(DB_QUERY_REGEXP, '_db_query_callback', $sql); 01285 $previous_db = db_set_active('chado'); 01286 $results = _db_query($sql); 01287 db_set_active($previous_db); 01288 return $results; 01289 } 01290 01291 /** 01292 * Get chado id for a node. E.g, if you want to get 'analysis_id' from the 01293 * 'analysis' table for a synced 'chado_analysis' node, use: 01294 * $analysis_id = chado_get_id_for_node ('analysis', $node) 01295 * Likewise, 01296 * $organism_id = chado_get_id_for_node ('organism', $node) 01297 * $feature_id = chado_get_id_for_node ('feature', $node) 01298 */ 01299 function chado_get_id_for_node ($table, $node) { 01300 return db_result(db_query("SELECT $table"."_id FROM {chado_".$table."} WHERE nid = $node->nid")); 01301 } 01302 01303 /** 01304 * Get node id for a chado feature/organism/analysis. E.g, if you want to 01305 * get the node id for an analysis, use: 01306 * $nid = chado_get_node_id ('analysis', $analysis_id) 01307 * Likewise, 01308 * $nid = chado_get_node_id ('organism', $organism_id) 01309 * $nid = chado_get_node_id ('feature', $feature_id) 01310 */ 01311 function chado_get_node_id ($table, $id) { 01312 return db_result(db_query("SELECT nid FROM {chado_".$table."} WHERE $table"."_id = $id")); 01313 } 01314 01315 /** 01316 * Retrieve a property for a given base table record 01317 * 01318 * @param $basetable 01319 * The base table for which the property should be retrieved. Thus to retrieve a property 01320 * for a feature the basetable=feature and property is retrieved from featureprop 01321 * @param $record_id 01322 * The primary key of the basetable to retrieve properties for. This should be in integer. 01323 * @param $property 01324 * The cvterm name describing the type of properties to be retrieved 01325 * @param $cv_name 01326 * The name of the cv that the above cvterm is part of 01327 * 01328 * @return 01329 * A chado variable with the specified properties expanded 01330 * 01331 * @ingroup tripal_chado_api 01332 */ 01333 function tripal_core_get_property($basetable, $record_id, $property, $cv_name){ 01334 01335 // get the foreign key for this property table 01336 $table_desc = module_invoke_all('chado_'.$basetable.'prop_schema'); 01337 $fkcol = key($table_desc['foreign keys'][$basetable]['columns']); 01338 01339 // construct the array of values to be inserted 01340 $values = array ( 01341 $fkcol => $record_id, 01342 'type_id' => array ( 01343 'cv_id' => array ( 01344 'name' => $cv_name, 01345 ), 01346 'name' => $property, 01347 'is_obsolete' => 0 01348 ), 01349 ); 01350 $results = tripal_core_generate_chado_var($basetable.'prop',$values); 01351 $results = tripal_core_expand_chado_vars($results,'field',$basetable.'prop.value'); 01352 return $results; 01353 } 01354 /** 01355 * Insert a property for a given basetable record 01356 * 01357 * @param $basetable 01358 * The base table for which the property should be inserted. Thus to insert a property 01359 * for a feature the basetable=feature and property is inserted into featureprop 01360 * @param $record_id 01361 * The primary key of the basetable to insert a property for. This should be in integer. 01362 * @param $property 01363 * The cvterm name describing the type of properties to be inserted 01364 * @param $cv_name 01365 * The name of the cv that the above cvterm is part of 01366 * @param $value 01367 * The value of the property to be inserted (can be empty) 01368 * @param $update_if_present 01369 * A boolean indicating whether an existing record should be updated or an error thrown 01370 * 01371 * @return 01372 * Return True on Insert/Update and False otherwise 01373 * 01374 * @ingroup tripal_chado_api 01375 */ 01376 function tripal_core_insert_property($basetable, $record_id, $property, 01377 $cv_name, $value, $update_if_present = 0) 01378 { 01379 // first see if the property already exists, if so we can't insert 01380 $prop = tripal_core_get_property($basetable,$record_id,$property,$cv_name); 01381 if(count($prop)>0){ 01382 if($update_if_present){ 01383 return tripal_core_update_property($basetable,$record_id,$property,$cv_name,$value) ; 01384 } else { 01385 return FALSE; 01386 } 01387 } 01388 01389 // get the foreign key for this property table 01390 $table_desc = module_invoke_all('chado_'.$basetable.'prop_schema'); 01391 $fkcol = key($table_desc['foreign keys'][$basetable]['columns']); 01392 01393 // construct the array of values to be inserted 01394 $values = array ( 01395 $fkcol => $record_id, 01396 'type_id' => array ( 01397 'cv_id' => array ( 01398 'name' => $cv_name, 01399 ), 01400 'name' => $property, 01401 'is_obsolete' => 0 01402 ), 01403 'value' => $value, 01404 'rank' => 0, 01405 ); 01406 return tripal_core_chado_insert($basetable.'prop',$values); 01407 } 01408 01409 /** 01410 * Update a property for a given basetable record 01411 * 01412 * @param $basetable 01413 * The base table for which the property should be updated. Thus to update a property 01414 * for a feature the basetable=feature and property is updated in featureprop 01415 * @param $record_id 01416 * The primary key of the basetable to update a property for. This should be in integer. 01417 * @param $property 01418 * The cvterm name describing the type of property to be updated 01419 * @param $cv_name 01420 * The name of the cv that the above cvterm is part of 01421 * @param $value 01422 * The value of the property to be inserted (can be empty) 01423 * @param $insert_if_missing 01424 * A boolean indicating whether a record should be inserted if one doesn't exist to update 01425 * 01426 * Note: The property to be updated is select via theu nique combination of $record_id and 01427 * $property and then it is updated with the supplied value 01428 * 01429 * @return 01430 * Return True on Update/Insert and False otherwise 01431 * 01432 * @ingroup tripal_chado_api 01433 */ 01434 function tripal_core_update_property($basetable, $record_id,$property,$cv_name, 01435 $value,$insert_if_missing = 0) 01436 { 01437 01438 // first see if the property is missing (we can't update a missing property 01439 $prop = tripal_core_get_property($basetable,$record_id,$property,$cv_name); 01440 if(count($prop)==0){ 01441 if($insert_if_missing){ 01442 return tripal_core_insert_property($basetable,$record_id,$property,$cv_name,$value); 01443 } else { 01444 return FALSE; 01445 } 01446 } 01447 01448 // get the foreign key for this property table 01449 $table_desc = module_invoke_all('chado_'.$basetable.'prop_schema'); 01450 $fkcol = key($table_desc['foreign keys'][$basetable]['columns']); 01451 01452 // construct the array that will match the exact record to update 01453 $match = array ( 01454 $fkcol => $record_id, 01455 'type_id' => array ( 01456 'cv_id' => array ( 01457 'name' => $cv_name, 01458 ), 01459 'name' => $property, 01460 ), 01461 ); 01462 // construct the array of values to be updated 01463 $values = array ( 01464 'value' => $value, 01465 ); 01466 return tripal_core_chado_update($basetable.'prop',$match,$values); 01467 } 01468 01469 /** 01470 * Deletes a property for a given basetable record 01471 * 01472 * @param $basetable 01473 * The base table for which the property should be deleted. Thus to deleted a property 01474 * for a feature the basetable=feature and property is deleted from featureprop 01475 * @param $record_id 01476 * The primary key of the basetable to delete a property for. This should be in integer. 01477 * @param $property 01478 * The cvterm name describing the type of property to be deleted 01479 * @param $cv_name 01480 * The name of the cv that the above cvterm is part of 01481 * 01482 * Note: The property to be deleted is select via theu nique combination of $record_id and $property 01483 * 01484 * @return 01485 * Return True on Delete and False otherwise 01486 * 01487 * @ingroup tripal_chado_api 01488 */ 01489 function tripal_core_delete_property($basetable, $record_id,$property,$cv_name){ 01490 // get the foreign key for this property table 01491 $table_desc = module_invoke_all('chado_'.$basetable.'prop_schema'); 01492 $fkcol = key($table_desc['foreign keys'][$basetable]['columns']); 01493 01494 // construct the array that will match the exact record to update 01495 $match = array ( 01496 $fkcol => $record_id, 01497 'type_id' => array ( 01498 'cv_id' => array ( 01499 'name' => $cv_name, 01500 ), 01501 'name' => $property, 01502 ), 01503 ); 01504 return tripal_core_chado_delete($basetable.'prop',$match); 01505 } 01506 01507 /** 01508 * This function is typically used in the '.install' file for a Tripal module 01509 * Each module should call this function during installation to create 01510 * the module data directory which is sites/default/files/tripal/[module_name] 01511 * for default Drupal settings. This directory can then be used by the module 01512 * for storing files. 01513 * 01514 * @param $module_name 01515 * the name of the module being installed. 01516 * 01517 * @returns 01518 * nothing 01519 * 01520 * @ingroup tripal_files_api 01521 */ 01522 function tripal_create_moddir($module_name){ 01523 // make the data directory for this module 01524 $data_dir = file_directory_path() . "/tripal/$module_name"; 01525 if(!file_check_directory($data_dir,FILE_CREATE_DIRECTORY|FILE_MODIFY_PERMISSIONS)){ 01526 $message = "Cannot create directory $data_dir. This module may not ". 01527 "behave correctly without this directory. Please create ". 01528 "the directory manually or fix the problem and reinstall."; 01529 drupal_set_message($message,'error'); 01530 watchdog('tripal_core',$message,array(),WATCHDOG_ERROR); 01531 } 01532 } 01533 01534 /** 01535 * Each Tripal module has a unique data directory which was creatd using the 01536 * tripal_create_moddir function during installation. This function 01537 * retrieves the directory path. 01538 * 01539 * @param $module_name 01540 * The name of the module 01541 * 01542 * @returns 01543 * The path within the Drupal installation where the data directory resides 01544 * @ingroup tripal_files_api 01545 */ 01546 function tripal_get_moddir($module_name){ 01547 $data_dir = file_directory_path() . "/tripal/$module_name"; 01548 return $data_dir; 01549 } 01550 /** 01551 * Set the Tripal Database 01552 * 01553 * The tripal_db_set_active function is used to prevent namespace collisions 01554 * when chado and drupal are installed in the same database but in different 01555 * schemas. It is also used for backwards compatibility with older versions 01556 * of tripal or in cases where chado is located outside of the Drupal database. 01557 * 01558 * @ingroup tripal_chado_api 01559 */ 01560 function tripal_db_set_active($dbname){ 01561 global $db_url, $db_type; 01562 $chado_exists = 0; 01563 01564 // only postgres can support search paths. So if this is MysQL then 01565 // just run the normal tripal_db_set_active function. 01566 if(strcmp($db_type,'pgsql')==0){ 01567 01568 // if the 'chado' database is in the $db_url variable then chado is 01569 // not in the same Drupal database 01570 if(is_array($db_url)){ 01571 if(isset($db_url[$dbname])){ 01572 return db_set_active($dbname); 01573 } 01574 } 01575 01576 // check to make sure the chado schema exists 01577 $sql = "select nspname from pg_catalog.pg_namespace where nspname = 'chado'"; 01578 if(db_fetch_object(db_query($sql))){ 01579 $chado_exists = 1; 01580 } 01581 01582 // here we make the assumption that the default database schema is 01583 // 'public'. This will most likely always be the case but if not, 01584 // then this code will break 01585 if($chado_exists && strcmp($dbname,'chado')==0){ 01586 db_query("set search_path to %s",'chado,public'); 01587 return 'public,chado'; 01588 } 01589 elseif($chado_exists) { 01590 db_query("set search_path to %s",'public,chado'); 01591 return 'chado,public'; 01592 } 01593 else { 01594 return db_set_active($dbname); 01595 } 01596 } 01597 else return db_set_active($dbname); 01598 } 01599 /** 01600 * Purpose: Get max rank for a given set of criteria 01601 * This function was developed with the many property tables in chado in mind 01602 * 01603 * @param $tablename 01604 * The name of the chado table you want to select the max rank from this table must contain a 01605 * rank column of type integer 01606 * @param $where_options 01607 * where options should include the id and type for that table to correctly 01608 * group a set of records together where the only difference are the value and rank 01609 * @code 01610 * array( 01611 * <column_name> => array( 01612 * 'type' => <type of column: INT/STRING>, 01613 * 'value' => <the value you want to filter on>, 01614 * 'exact' => <if TRUE use =; if FALSE use ~>, 01615 * ) 01616 * ) 01617 * @endcode 01618 * @return the maximum rank 01619 * 01620 * @ingroup tripal_chado_api 01621 */ 01622 function tripal_get_max_chado_rank ($tablename, $where_options) { 01623 01624 $where= array(); 01625 //generate the where clause from supplied options 01626 // the key is the column name 01627 foreach ($where_options as $key => $val_array) { 01628 if (preg_match('/INT/', $val_array['type'])) { 01629 $where[] = $key."=".$val_array['value']; 01630 } else { 01631 if ($val_array['exact']) { $operator='='; } 01632 else { $operator='~'; } 01633 $where[] = $key.$operator."'".$val_array['value']."'"; 01634 } 01635 } 01636 01637 $previous_db = tripal_db_set_active('chado'); 01638 $result = db_fetch_object(db_query( 01639 "SELECT max(rank) as max_rank, count(rank) as count FROM %s WHERE %s", 01640 $tablename, 01641 implode(' AND ',$where) 01642 )); 01643 tripal_db_set_active($previous_db); 01644 //drupal_set_message("Max Rank Query=SELECT max(rank) as max_rank, count(rank) as count FROM ".$tablename." WHERE ".implode(' AND ',$where)); 01645 if ($result->count > 0) { 01646 return $result->max_rank; 01647 } else { 01648 return -1; 01649 } 01650 }