tripal_core.properties.api.inc 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. <?php
  2. /**
  3. * @file
  4. * API to manage the chado prop table for various Tripal Node Types
  5. *
  6. * How To Use:
  7. * @code
  8. function chado_example_form($form, $form_state) {
  9. // Default values for form elements can come in the following ways:
  10. //
  11. // 1) as elements of the $node object. This occurs when editing an existing node
  12. // 2) in the $form_state['values'] array which occurs on a failed validation or
  13. // ajax callbacks when the ajax call originates from non-submit fields other
  14. // than button
  15. // 3) in the $form_state['input'] array which occurs on ajax callbacks from submit
  16. // form elements (e.g. buttons) and the form is being rebuilt but has not yet
  17. // been validated
  18. //
  19. // The properties elements added by this function do use AJAX calls from buttons,
  20. // therefore, it is important to check for form values in the $form_state['values']
  21. // for case #2 above, and in the $form_state['input'] for case #3.
  22. // See the chado analysis node form for an example.
  23. // Next, add in all the form array definition particular to your node type
  24. // To add in the properties form elements, you first need to prepare the arguments
  25. // for the function call. One inportant argument is a list of properties that
  26. // will be made avaialble for the user to select from. You should query the
  27. // database to retrieve the applicable terms from the cvterm table and store them in an array
  28. // of where the cvterm.cvterm_id field is the key and the cvterm.name is the value.
  29. // these terms should all be from the same vocabulary.
  30. $properties = array();
  31. $properties[] = 'Select a Property';
  32. $sql = "
  33. SELECT DISTINCT CVT.cvterm_id, CVT.name, CVT.definition
  34. FROM {cvterm} CVT
  35. INNER JOIN {cv} CV ON CVT.cv_id = CV.cv_id
  36. WHERE
  37. CV.name = :ontology_name AND
  38. NOT CVT.is_obsolete = 1
  39. ORDER BY CVT.name ASC
  40. ";
  41. $ontology_name = 'name_of_proptype_ontology'; //you need to set this variable with the cv.name of the ontology governing your prop tables type_id
  42. $prop_types = chado_query($sql, array(':ontology_name' => $ontology_name));
  43. while ($prop = $prop_types->fetchObject()) {
  44. $properties[$prop->cvterm_id] = $prop->name;
  45. }
  46. // the properties form will add a select dropdown of terms containing the items in the $properties array
  47. // constructed above, but it will also pre-populate rows of properties that already are associated
  48. // with the object. If you would like to pre-populated properties regardless if they exist in the database
  49. // or not, you can create an $include array which has the following format:
  50. // array(
  51. // array('cvterm' => $obj1, 'value' => $val1),
  52. // array('cvterm' => $obj2, 'value' => $val2),
  53. // ... etc
  54. // );
  55. // The 'cvterm' key should have as a value an object with these properties: 'name', 'cvterm_id', 'definition'.
  56. $include = array();
  57. // sometimes a property exists in the database and is properly associated with the object, but we do
  58. // not want it to appear in the list of properties that are pre-populated. It may be handled in some
  59. // other way. For example, for contacts, the description field is stored as a property because
  60. // the actual contact.description field is only 255 characters. The 'contact_description' property should
  61. // not be shown in the list of properties, even if present, because it is handled by
  62. // a different form element. This array holds the value of the cvterm.name column of the cvterms
  63. // to exclude
  64. $exclude = array();
  65. // the instructions argument provides additional instructions to the user beyond the default instructions.
  66. $instructions = t('To add additional properties to the drop down. ' . l("Add terms to the $ontology_name vocabulary", "admin/tripal/chado/tripal_cv/cvterm/add") . ".");
  67. // Finally, and add the properties form elements to the form
  68. tripal_core_properties_form(
  69. $form, $form_state, // form and form_state of the current form
  70. 'exampleprop', // properties table name
  71. 'example_id', // key to link to the chado content created by this node
  72. $ontology_name, // name of ontology governing your prop table type_id column
  73. $properties, // an array of properties to use in the drop-down
  74. $example_id, // the value of the above key
  75. $exclude, // elements from the ontology you don't want to be available as property types
  76. $include, // additional elements not in the ontology that you do what in the drop-down
  77. $instructions, // form specific instructions
  78. 'Properties' // name of the fieldset
  79. );
  80. return $form;
  81. }
  82. function chado_example_insert($node) {
  83. // if there is an example_id in the $node object then this must be a sync so
  84. // we can skip adding the chado_example as it is already there, although
  85. // we do need to proceed with the rest of the insert
  86. if (!property_exists($node, 'example_id')) {
  87. // Add record to chado example table
  88. // Add to any other tables needed
  89. // Add each property (exampleprop table). The tripal_core_properties_form_retrieve()
  90. // function retrieves all of the properties and returns them in an array of the format:
  91. //
  92. // $properties[<property name>][<rank>] = <value
  93. //
  94. // This array can then be used for inserting or updating properties using the API call
  95. // tripal_hook_insert_property()
  96. //
  97. // example_property = controlled vocab name for exampleprop.type_id
  98. $properties = tripal_core_properties_form_retreive($node, 'example_property');
  99. foreach ($properties as $property => $elements) {
  100. foreach ($elements as $rank => $value) {
  101. $success = tripal_core_insert_property(
  102. 'example', //base table name
  103. $example_id, // key to link to the chado content created by this node
  104. $property, // cvterm.name of the property to be added
  105. $ontology_name, // name of the ontology the cvterm is from
  106. $value // the value o the property
  107. );
  108. if (!$success) {
  109. watchdog(
  110. 'tripal_example',
  111. 'Example Update: Unable to insert property %cvterm %value.',
  112. array('%cvterm' => $property, '%value' => $value),
  113. WATCHDOG_ERROR
  114. );
  115. }
  116. }
  117. }
  118. }
  119. // Add record to chado_example linking example_id to new node
  120. }
  121. function chado_example_update($node) {
  122. // Update record in chado example table
  123. // Update any other tables needed
  124. // First delete any existing properties
  125. tripal_core_chado_delete(
  126. 'exampleprop', // the name of the prop table
  127. array('example_id' => $example_id) // name of your key and the current value used to determine which to delete
  128. );
  129. // Add each property (exampleprop table)
  130. // example_property = controlled vocab name for exampleprop.type_id
  131. $properties = tripal_core_properties_form_retreive($node, 'example_property');
  132. foreach ($properties as $property => $elements) {
  133. foreach ($elements as $rank => $value) {
  134. $success = tripal_core_insert_property(
  135. 'example', //base table name
  136. $example_id, // key to link to the chado content created by this node
  137. $property, // cvterm.name of the property to be added
  138. $ontology_name, // name of the ontology the cvterm is from
  139. $value // the value o the property
  140. );
  141. if (!$success) {
  142. watchdog(
  143. 'tripal_example',
  144. 'Example Update: Unable to insert property %cvterm %value.',
  145. array('%cvterm' => $property, '%value' => $value),
  146. WATCHDOG_ERROR
  147. );
  148. }
  149. }
  150. }
  151. }
  152. // Don't need to update chado_example linking table since niether example_id or nid can be changed in update
  153. }
  154. * @endcode
  155. */
  156. /**
  157. * Retrieve a property for a given base table record
  158. *
  159. * @param $basetable
  160. * The base table for which the property should be retrieved. Thus to retrieve a property
  161. * for a feature the basetable=feature and property is retrieved from featureprop
  162. * @param $record_id
  163. * The foriegn key field of the base table. This should be in integer.
  164. * @param $property
  165. * The cvterm name describing the type of properties to be retrieved
  166. * @param $cv_name
  167. * The name of the cv that the above cvterm is part of
  168. *
  169. * @return
  170. * An array in the same format as that generated by the function
  171. * tripal_core_generate_chado_var(). If only one record is returned it
  172. * is a single object. If more than one record is returned then it is an array
  173. * of objects
  174. *
  175. * @ingroup tripal_properties_api
  176. */
  177. function tripal_core_get_property($basetable, $record_id, $property, $cv_name) {
  178. // get the foreign key for this property table
  179. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  180. $fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
  181. // construct the array of values to be selected
  182. $values = array(
  183. $fkcol => $record_id,
  184. 'type_id' => array(
  185. 'cv_id' => array(
  186. 'name' => $cv_name,
  187. ),
  188. 'name' => $property,
  189. 'is_obsolete' => 0
  190. ),
  191. );
  192. $results = tripal_core_generate_chado_var($basetable . 'prop', $values);
  193. if ($results) {
  194. $results = tripal_core_expand_chado_vars($results, 'field', $basetable . 'prop.value');
  195. }
  196. return $results;
  197. }
  198. /**
  199. * Insert a property for a given base table. By default if the property already
  200. * exists a new property is added with the next available rank. If
  201. * $update_if_present argument is specified then the record will be updated if it
  202. * exists rather than adding a new property.
  203. *
  204. * @param $basetable
  205. * The base table for which the property should be inserted. Thus to insert a property
  206. * for a feature the basetable=feature and property is inserted into featureprop
  207. * @param $record_id
  208. * The foriegn key value of the base table. This should be in integer.
  209. * @param $property
  210. * The cvterm name describing the type of properties to be inserted
  211. * @param $cv_name
  212. * The name of the cv that the above cvterm is part of
  213. * @param $value
  214. * The value of the property to be inserted (can be empty)
  215. * @param $update_if_present
  216. * A boolean indicating whether an existing record should be updated. If the
  217. * property already exists and this value is not specified or is zero then
  218. * a new property will be added with the next largest rank.
  219. *
  220. * @return
  221. * Return True on Insert/Update and False otherwise
  222. *
  223. * @ingroup tripal_properties_api
  224. */
  225. function tripal_core_insert_property($basetable, $record_id, $property,
  226. $cv_name, $value, $update_if_present = 0) {
  227. // first see if the property already exists, if the user want's to update
  228. // then we can do that, but otherwise we want to increment the rank and
  229. // insert
  230. $props = tripal_core_get_property($basetable, $record_id, $property, $cv_name);
  231. if (!is_array($props) and $props) {
  232. $props = array($props);
  233. }
  234. $rank = 0;
  235. if (count($props) > 0) {
  236. if ($update_if_present) {
  237. return tripal_core_update_property($basetable, $record_id, $property, $cv_name, $value);
  238. }
  239. else {
  240. // iterate through the properties returned and check to see if the
  241. // property with this value already exists if not, get the largest rank
  242. // and insert the same property but with this new value
  243. foreach ($props as $p) {
  244. if ($p->rank > $rank) {
  245. $rank = $p->rank;
  246. }
  247. if (strcmp($p->value, $value) == 0) {
  248. return TRUE;
  249. }
  250. }
  251. // now add 1 to the rank
  252. $rank++;
  253. }
  254. }
  255. // make sure the cvterm exists. Otherwise we'll get an error with
  256. // prepared statements not matching
  257. $values = array(
  258. 'cv_id' => array(
  259. 'name' => $cv_name,
  260. ),
  261. 'name' => $property,
  262. );
  263. $options = array();
  264. $term = tripal_core_chado_select('cvterm', array('cvterm_id'), $values, $options);
  265. if (!$term or count($term) == 0) {
  266. watchdog('tripal_core', "Cannot find property '%prop_name' in vocabulary '%cvname'.",
  267. array('%prop_name' => $property, '%cvname' => $cv_name), WATCHDOG_ERROR);
  268. return FALSE;
  269. }
  270. // get the foreign key for this property table
  271. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  272. $fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
  273. // construct the array of values to be inserted
  274. $values = array(
  275. $fkcol => $record_id,
  276. 'type_id' => array(
  277. 'cv_id' => array(
  278. 'name' => $cv_name,
  279. ),
  280. 'name' => $property,
  281. ),
  282. 'value' => $value,
  283. 'rank' => $rank,
  284. );
  285. $options = array();
  286. $result = tripal_core_chado_insert($basetable . 'prop', $values, $options);
  287. return $result;
  288. }
  289. /**
  290. * Update a property for a given base table record and property name. This
  291. * function should be used only if one record of the property will be present.
  292. * If the property name can have multiple entries (with increasing rank) then
  293. * use the function named tripal_core_update_property_by_id
  294. *
  295. * @param $basetable
  296. * The base table for which the property should be updated. The property table
  297. * is constructed using a combination of the base table name and the suffix
  298. * 'prop' (e.g. basetable = feature then property tabie is featureprop).
  299. * @param $record_id
  300. * The foreign key of the basetable to update a property for. This should be in integer.
  301. * For example, if the basetable is 'feature' then the $record_id should be the feature_id
  302. * @param $property
  303. * The cvterm name of property to be updated
  304. * @param $cv_name
  305. * The name of the cv that the above cvterm is part of
  306. * @param $value
  307. * The value of the property to be inserted (can be empty)
  308. * @param $insert_if_missing
  309. * A boolean indicating whether a record should be inserted if one doesn't exist to update
  310. *
  311. * Note: The property to be updated is select via the unique combination of $record_id and
  312. * $property and then it is updated with the supplied value
  313. *
  314. * @return
  315. * Return True on Update/Insert and False otherwise
  316. *
  317. * @ingroup tripal_properties_api
  318. */
  319. function tripal_core_update_property($basetable, $record_id, $property,
  320. $cv_name, $value, $insert_if_missing = 0) {
  321. // first see if the property is missing (we can't update a missing property
  322. $prop = tripal_core_get_property($basetable, $record_id, $property, $cv_name);
  323. if (count($prop)==0) {
  324. if ($insert_if_missing) {
  325. return tripal_core_insert_property($basetable, $record_id, $property, $cv_name, $value);
  326. }
  327. else {
  328. return FALSE;
  329. }
  330. }
  331. // get the foreign key for this property table
  332. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  333. $fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
  334. // construct the array that will match the exact record to update
  335. $match = array(
  336. $fkcol => $record_id,
  337. 'type_id' => array(
  338. 'cv_id' => array(
  339. 'name' => $cv_name,
  340. ),
  341. 'name' => $property,
  342. ),
  343. );
  344. // construct the array of values to be updated
  345. $values = array(
  346. 'value' => $value,
  347. );
  348. return tripal_core_chado_update($basetable . 'prop', $match, $values);
  349. }
  350. /**
  351. * Update a property for a given base table record. This function should be
  352. * used if multiple records of the same property will be present. Also, use this
  353. * function to change the property name of an existing property.
  354. *
  355. * @param $basetable
  356. * The base table for which the property should be updated. The property table
  357. * is constructed using a combination of the base table name and the suffix
  358. * 'prop' (e.g. basetable = feature then property tabie is featureprop).
  359. * @param $record_id
  360. * The primary key of the base table. This should be in integer.
  361. * For example, if the basetable is 'feature' then the $record_id should be the featureprop_id
  362. * @param $property
  363. * The cvterm name of property to be updated
  364. * @param $cv_name
  365. * The name of the cv that the above cvterm is part of
  366. * @param $value
  367. * The value of the property to be inserted (can be empty)
  368. *
  369. * @return
  370. * Return True on Update/Insert and False otherwise
  371. *
  372. * @ingroup tripal_properties_api
  373. */
  374. function tripal_core_update_property_by_id($basetable, $record_id, $property,
  375. $cv_name, $value) {
  376. // get the primary key for this property table
  377. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  378. $pkcol = $table_desc['primary key'][0];
  379. // construct the array that will match the exact record to update
  380. $match = array(
  381. $pkcol => $record_id,
  382. );
  383. // construct the array of values to be updated
  384. $values = array(
  385. 'type_id' => array(
  386. 'cv_id' => array(
  387. 'name' => $cv_name,
  388. ),
  389. 'name' => $property,
  390. ),
  391. 'value' => $value,
  392. );
  393. return tripal_core_chado_update($basetable . 'prop', $match, $values);
  394. }
  395. /**
  396. * Deletes a property for a given base table record using the property name
  397. *
  398. * @param $basetable
  399. * The base table for which the property should be deleted. Thus to deleted a property
  400. * for a feature the basetable=feature and property is deleted from featureprop
  401. * @param $record_id
  402. * The primary key of the basetable to delete a property for. This should be in integer.
  403. * @param $property
  404. * The cvterm name describing the type of property to be deleted
  405. * @param $cv_name
  406. * The name of the cv that the above cvterm is part of
  407. *
  408. * Note: The property to be deleted is select via the unique combination of $record_id and $property
  409. *
  410. * @return
  411. * Return True on Delete and False otherwise
  412. *
  413. * @ingroup tripal_properties_api
  414. */
  415. function tripal_core_delete_property($basetable, $record_id, $property, $cv_name) {
  416. // get the foreign key for this property table
  417. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  418. $fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
  419. // construct the array that will match the exact record to update
  420. $match = array(
  421. $fkcol => $record_id,
  422. 'type_id' => array(
  423. 'cv_id' => array(
  424. 'name' => $cv_name,
  425. ),
  426. 'name' => $property,
  427. ),
  428. );
  429. return tripal_core_chado_delete($basetable . 'prop', $match);
  430. }
  431. /**
  432. * Deletes a property using the property ID
  433. *
  434. * @param $basetable
  435. * The base table for which the property should be deleted. Thus to deleted a property
  436. * for a feature the basetable=feature and property is deleted from featureprop
  437. * @param $record_id
  438. * The primary key of the basetable to delete a property for. This should be in integer.
  439. *
  440. * @return
  441. * Return True on Delete and False otherwise
  442. *
  443. * @ingroup tripal_properties_api
  444. */
  445. function tripal_core_delete_property_by_id($basetable, $record_id) {
  446. // get the foreign key for this property table
  447. $table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
  448. $pkcol = $table_desc['primary key'][0];
  449. // construct the array that will match the exact record to update
  450. $match = array(
  451. $pkcol => $record_id,
  452. );
  453. return tripal_core_chado_delete($basetable . 'prop', $match);
  454. }