sbo__relationship.inc 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198
  1. <?php
  2. class sbo__relationship extends ChadoField {
  3. // --------------------------------------------------------------------------
  4. // EDITABLE STATIC CONSTANTS
  5. //
  6. // The following constants SHOULD be set for each descendent class. They are
  7. // used by the static functions to provide information to Drupal about
  8. // the field and it's default widget and formatter.
  9. // --------------------------------------------------------------------------
  10. // The default label for this field.
  11. public static $default_label = 'Relationship';
  12. // The default description for this field.
  13. public static $description = 'Relationships between records.';
  14. // Provide a list of instance specific settings. These can be accessed within
  15. // the instanceSettingsForm. When the instanceSettingsForm is submitted
  16. // then Drupal will automatically change these settings for the instance.
  17. // It is recommended to put settings at the instance level whenever possible.
  18. // If you override this variable in a child class be sure to replicate the
  19. // term_name, term_vocab, term_accession and term_fixed keys as these are
  20. // required for all TripalFields.
  21. public static $default_instance_settings = [
  22. // The short name for the vocabulary (e.g. schema, SO, GO, PATO, etc.).
  23. 'term_vocabulary' => 'SBO',
  24. // The name of the term.
  25. 'term_name' => 'Relationship',
  26. // The unique ID (i.e. accession) of the term.
  27. 'term_accession' => '0000374',
  28. // Set to TRUE if the site admin is allowed to change the term
  29. // type. This will create form elements when editing the field instance
  30. // to allow the site admin to change the term settings above.
  31. 'term_fixed' => FALSE,
  32. // Inidates if this field should be automatically attached to display
  33. // or web services or if this field should be loaded separately. This
  34. // is convenient for speed. Fields that are slow should for loading
  35. // should ahve auto_attach set to FALSE so tha their values can be
  36. // attached asyncronously.
  37. 'auto_attach' => FALSE,
  38. // Settings to help the site admin control how relationship types and
  39. // valid subject/objects can be selected by the user.
  40. 'relationships' => [
  41. 'option1_vocabs' => '',
  42. 'option2_vocab' => '',
  43. 'option2_parent' => '',
  44. 'relationship_types' => '',
  45. ],
  46. // The number of items to show on a page.
  47. 'items_per_page' => 10,
  48. ];
  49. // The default widget for this field.
  50. public static $default_widget = 'sbo__relationship_widget';
  51. // The default formatter for this field.
  52. public static $default_formatter = 'sbo__relationship_formatter';
  53. // --------------------------------------------------------------------------
  54. // PROTECTED CLASS MEMBERS -- DO NOT OVERRIDE
  55. // --------------------------------------------------------------------------
  56. // An array containing details about the field. The format of this array
  57. // is the same as that returned by field_info_fields()
  58. protected $field;
  59. // An array containing details about an instance of the field. A field does
  60. // not have to have an instance. But if dealing with an instance (such as
  61. // when using the widgetForm, formatterSettingsForm, etc.) it should be set.
  62. protected $instance;
  63. // An array of columns to use as the "name" of the subject and object.
  64. // For example, for the feature table, this will be the name,
  65. // whereas, for the organism table this will be the genus & species.
  66. protected $base_name_columns;
  67. // One of 'type_id', or 'table_name'. Not all base tables have a type_id so
  68. // this setting allows us to better handle these cases.
  69. protected $base_type_column;
  70. // This field depends heavily on the schema of the relationship and base
  71. // table. The following variables cache the schema to greatly speed up
  72. // this field.
  73. // Note: both are ChadoSchema objects.
  74. protected $schema;
  75. protected $base_schema;
  76. // The column which indicated the subject/object_id in the current
  77. // relationship table. This allows us to support exceptions in the common
  78. // chado naming conventions.
  79. protected $subject_id_column;
  80. protected $object_id_column;
  81. /**
  82. * @see TripalField::elements()
  83. */
  84. public function elementInfo() {
  85. $field_term = $this->getFieldTermID();
  86. return [
  87. $field_term => [
  88. 'operations' => ['eq', 'contains', 'starts'],
  89. 'sortable' => FALSE,
  90. 'searchable' => FALSE,
  91. 'type' => 'xs:complexType',
  92. 'readonly' => FALSE,
  93. 'elements' => [
  94. 'SIO:000493' => [
  95. 'searchable' => FALSE,
  96. 'name' => 'relationship_clause',
  97. 'label' => 'Relationship Clause',
  98. 'help' => 'An English phrase describing the relationships.',
  99. 'sortable' => FALSE,
  100. 'type' => 'xs:string',
  101. 'readonly' => TRUE,
  102. 'required' => FALSE,
  103. ],
  104. 'local:relationship_subject' => [
  105. 'searchable' => FALSE,
  106. 'name' => 'relationship_subject',
  107. 'operations' => ['eq', 'ne', 'contains', 'starts'],
  108. 'sortable' => FALSE,
  109. 'type' => 'xs:complexType',
  110. 'readonly' => FALSE,
  111. 'required' => TRUE,
  112. 'elements' => [
  113. 'rdfs:type' => [
  114. 'name' => 'type',
  115. 'searchable' => TRUE,
  116. 'label' => 'Relationship Subject Type',
  117. 'help' => 'The subject\'s data type in a relationship clause',
  118. 'operations' => ['eq', 'ne', 'contains', 'starts'],
  119. 'sortable' => TRUE,
  120. 'type' => 'xs:string',
  121. 'readonly' => FALSE,
  122. 'required' => TRUE,
  123. ],
  124. 'schema:name' => [
  125. 'name' => 'name',
  126. 'searchable' => TRUE,
  127. 'label' => 'Relationship Subject Name',
  128. 'help' => 'The subject\'s name in a relationship clause',
  129. 'operations' => ['eq', 'ne', 'contains', 'starts'],
  130. 'sortable' => TRUE,
  131. 'type' => 'xs:string',
  132. 'readonly' => FALSE,
  133. 'required' => TRUE,
  134. ],
  135. 'entity' => [
  136. 'searchable' => FALSE,
  137. 'sortable' => FALSE,
  138. ],
  139. ],
  140. ],
  141. 'local:relationship_type' => [
  142. 'searchable' => TRUE,
  143. 'name' => 'relationship_type',
  144. 'operations' => ['eq', 'ne', 'contains', 'starts'],
  145. 'sortable' => TRUE,
  146. 'type' => 'xs:string',
  147. 'readonly' => FALSE,
  148. 'required' => TRUE,
  149. ],
  150. 'local:relationship_object' => [
  151. 'searchable' => FALSE,
  152. 'name' => 'relationship_object',
  153. 'operations' => ['eq', 'ne', 'contains', 'starts'],
  154. 'sortable' => FALSE,
  155. 'type' => 'xs:complexType',
  156. 'readonly' => FALSE,
  157. 'required' => TRUE,
  158. 'elements' => [
  159. 'rdfs:type' => [
  160. 'searchable' => TRUE,
  161. 'name' => 'object_type',
  162. 'label' => 'Relationship Object Type',
  163. 'help' => 'The objects\'s data type in a relationship clause',
  164. 'operations' => ['eq', 'ne', 'contains', 'starts'],
  165. 'sortable' => TRUE,
  166. 'type' => 'xs:string',
  167. 'readonly' => FALSE,
  168. 'required' => TRUE,
  169. ],
  170. 'schema:name' => [
  171. 'searchable' => TRUE,
  172. 'name' => 'object_name',
  173. 'label' => 'Relationship Object Name',
  174. 'help' => 'The objects\'s name in a relationship clause',
  175. 'operations' => ['eq', 'ne', 'contains', 'starts'],
  176. 'sortable' => TRUE,
  177. 'type' => 'xs:string',
  178. 'readonly' => FALSE,
  179. 'required' => TRUE,
  180. ],
  181. 'entity' => [
  182. 'searchable' => FALSE,
  183. 'sortable' => FALSE,
  184. ],
  185. ],
  186. ],
  187. ],
  188. ],
  189. ];
  190. }
  191. /**
  192. * Extends TripalField::__construct().
  193. */
  194. public function __construct($field, $instance) {
  195. parent::__construct($field, $instance);
  196. $reltable = $instance['settings']['chado_table'];
  197. $base_table = $instance['settings']['base_table'];
  198. // First, initialize the schema's.
  199. $this->schema = new ChadoSchema();
  200. $this->schema = $this->schema->getTableSchema($reltable);
  201. $this->base_schema = new ChadoSchema();
  202. $this->base_schema = $this->base_schema->getTableSchema($base_table);
  203. // Determine the subject_id/object_id column names.
  204. foreach ($this->schema['foreign keys'][$base_table]['columns'] AS $lcolum => $rcolum) {
  205. if (preg_match('/^subject_.*id/', $lcolum)) {
  206. $this->subject_id_column = $lcolum;
  207. }
  208. else {
  209. if (preg_match('/^object_.*id/', $lcolum)) {
  210. $this->object_id_column = $lcolum;
  211. }
  212. }
  213. }
  214. // Determine the name and type columns.
  215. $this->base_name_columns = [];
  216. $this->base_type_column = 'table_name';
  217. switch ($instance['settings']['chado_table']) {
  218. // TODO: note that Chado 1.4 will add types to, at least,
  219. // project and analysis, at which point you should use the default instead.
  220. case 'acquisition_relationship':
  221. case 'analysis_relationship':
  222. case 'biomaterial_relationship':
  223. case 'cell_line_relationship':
  224. case 'quantification_relationship':
  225. case 'element_relationship':
  226. case 'project_relationship':
  227. $this->base_name_columns = ['name'];
  228. $this->base_type_column = 'table_name';
  229. break;
  230. case 'pub_relationship':
  231. $this->base_name_columns = ['title'];
  232. $this->base_type_column = 'table_name';
  233. break;
  234. case 'organism_relationship':
  235. $this->base_name_columns = ['genus', 'species'];
  236. $this->base_type_column = 'table_name';
  237. break;
  238. case 'phylonode_relationship':
  239. $this->base_name_columns = ['label'];
  240. $this->base_type_column = 'table_name';
  241. break;
  242. case 'contact':
  243. $this->base_name_columns = ['name'];
  244. $this->base_type_column = 'type_id';
  245. break;
  246. default:
  247. // @todo update this to use the schema.
  248. $this->base_name_columns = ['name'];
  249. $this->base_type_column = 'type_id';
  250. }
  251. }
  252. /**
  253. * Retrive the subject from the current relationship.
  254. *
  255. * @param $relationship
  256. * A single expanded relationship from a variable generated by
  257. * chado_generate_var(). At a minimum, if will have a subject, object and
  258. * type which should be expanded to the appropriate type of record
  259. * depending on the content type this widget is attached to.
  260. *
  261. * @return
  262. * An array of information for the subject of the $relationship.
  263. */
  264. private function getRelationshipSubject($relationship) {
  265. $name = [];
  266. foreach ($this->base_name_columns as $column) {
  267. $name[] = $relationship->{$this->subject_id_column}->{$column};
  268. }
  269. // Retrieve the type.
  270. $type = $this->instance['settings']['base_table'];
  271. if (($this->base_type_column != 'table_name') AND isset($relationship->{$this->subject_id_column}->{$this->base_type_column})) {
  272. $type_object = $relationship->{$this->subject_id_column}->{$this->base_type_column};
  273. if (isset($type_object->name)) {
  274. $type = $type_object->name;
  275. }
  276. elseif (isset($type_object->uniquename)) {
  277. $type = $type_object->uniquename;
  278. }
  279. }
  280. $record = [
  281. 'rdfs:type' => $type,
  282. 'schema:name' => implode(' ', $name),
  283. ];
  284. // If the object has a uniquename then add that in for reference.
  285. if (property_exists($relationship->{$this->subject_id_column}, 'uniquename')) {
  286. $record['data:0842'] = $relationship->{$this->subject_id_column}->uniquename;
  287. }
  288. // If the object has an organism then add that in for reference.
  289. if (property_exists($relationship->{$this->subject_id_column}, 'organism_id')
  290. AND is_object($relationship->{$this->subject_id_column}->organism_id)) {
  291. $record['OBI:0100026'] = $relationship->{$this->subject_id_column}->organism_id->genus . ' ' . $relationship->{$this->subject_id_column}->organism_id->species;
  292. }
  293. // Add in the TripalEntity ids if the object is published.
  294. if (property_exists($relationship->{$this->subject_id_column}, 'entity_id')) {
  295. $entity_id = $relationship->{$this->subject_id_column}->entity_id;
  296. $record['entity'] = 'TripalEntity:' . $entity_id;
  297. }
  298. return $record;
  299. }
  300. /**
  301. * Retrieve the object from the current relationship.
  302. *
  303. * @param $relationship
  304. * A single expanded relationship from a variable generated by
  305. * chado_generate_var(). At a minimum, if will have a subject, object and
  306. * type which should be expanded to the appropriate type of record
  307. * depending on the content type this widget is attached to.
  308. *
  309. * @return
  310. * An array of information for the object of the $relationship.
  311. */
  312. private function getRelationshipObject($relationship) {
  313. $name = [];
  314. // Retrieve the name (may be multiple parts).
  315. foreach ($this->base_name_columns as $column) {
  316. $name[] = $relationship->{$this->object_id_column}->{$column};
  317. }
  318. // Retrieve the Type.
  319. $type = $this->instance['settings']['base_table'];
  320. if (($this->base_type_column != 'table_name') AND isset($relationship->{$this->object_id_column}->{$this->base_type_column})) {
  321. $type_object = $relationship->{$this->object_id_column}->{$this->base_type_column};
  322. if (isset($type_object->name)) {
  323. $type = $type_object->name;
  324. }
  325. elseif (isset($type_object->uniquename)) {
  326. $type = $type_object->uniquename;
  327. }
  328. }
  329. $record = [
  330. 'rdfs:type' => $type,
  331. 'schema:name' => implode(' ', $name),
  332. ];
  333. // If the object has a unqiuename then add that in for reference.
  334. if (property_exists($relationship->{$this->object_id_column}, 'uniquename')) {
  335. $record['data:0842'] = $relationship->{$this->object_id_column}->uniquename;
  336. }
  337. // If the object has an organism then add that in for reference.
  338. if (property_exists($relationship->{$this->object_id_column}, 'organism_id')
  339. AND is_object($relationship->{$this->object_id_column}->organism_id)) {
  340. $record['OBI:0100026'] = $relationship->{$this->object_id_column}->organism_id->genus . ' ' . $relationship->{$this->object_id_column}->organism_id->species;
  341. }
  342. // Add in the TripalEntity ids if the object is published.
  343. if (property_exists($relationship->{$this->object_id_column}, 'entity_id')) {
  344. $entity_id = $relationship->{$this->object_id_column}->entity_id;
  345. $record['entity'] = 'TripalEntity:' . $entity_id;
  346. }
  347. return $record;
  348. }
  349. /**
  350. * Load a specific relationship as indicated by $delta.
  351. * This function is called by the load method below.
  352. *
  353. * Note: The relationship is loaded by adding it the the entitiy values.
  354. *
  355. * @param $relationship
  356. * A single expanded relationship from a variable generated by
  357. * chado_generate_var(). At a minimum, if will have a subject, object and
  358. * type which should be expanded to the appropriate type of record
  359. * depending on the content type this widget is attached to.
  360. * @param $entity
  361. * The entity the widget is attached to.
  362. * @param $delta
  363. * An integer indicating the specific relationship to load. This is usually
  364. * the rank from the relationship table (if there is one).
  365. */
  366. private function loadRelationship($relationship, &$entity, $delta) {
  367. $field_name = $this->field['field_name'];
  368. $field_table = $this->instance['settings']['chado_table'];
  369. $base_table = $this->instance['settings']['base_table'];
  370. $rel_acc = $relationship->type_id->dbxref_id->db_id->name . ':' . $relationship->type_id->dbxref_id->accession;
  371. $rel_type = $relationship->type_id->name;
  372. $verb = $this->get_rel_verb($rel_type);
  373. $pkey = $this->schema['primary key'][0];
  374. $subject_id_key = $this->subject_id_column;
  375. $object_id_key = $this->object_id_column;
  376. // @todo grab these separately like it was before.
  377. $subject_pkey = $object_pkey = $this->base_schema['primary key'][0];
  378. // A publication title can exceed the character limit for chado_expand_var()
  379. // so make sure it has been added.
  380. // TODO Should this step be generalized to other content type that could have a long name?
  381. if (in_array('pub.title', $relationship->expandable_fields)) {
  382. $relationship = chado_expand_var($relationship, 'field', 'pub.title', []);
  383. }
  384. $entity->{$field_name}['und'][$delta]['value'] = [
  385. 'local:relationship_subject' => $this->getRelationshipSubject($relationship),
  386. 'local:relationship_type' => $relationship->type_id->name,
  387. 'local:relationship_object' => $this->getRelationshipObject($relationship),
  388. ];
  389. // Add the clause to the values array. The clause is a written version
  390. // of the relationships.
  391. $rel_type_clean = lcfirst(preg_replace('/_/', ' ', $rel_type));
  392. $subject_type = $entity->{$field_name}['und'][$delta]['value']['local:relationship_subject']['rdfs:type'];
  393. $subject_name = $entity->{$field_name}['und'][$delta]['value']['local:relationship_subject']['schema:name'];
  394. $object_type = $entity->{$field_name}['und'][$delta]['value']['local:relationship_object']['rdfs:type'];
  395. $object_name = $entity->{$field_name}['und'][$delta]['value']['local:relationship_object']['schema:name'];
  396. // Remember the current entity could be either the subject or object!
  397. // Example: The genetic_marker, MARKER1 , derives from the sequence_variant, VARIANT1.
  398. // The above relationship will be shown both on marker and variant pages
  399. // and as such both subject and object names need to be shown.
  400. // Keep the clause as an array for now so we can apply formatting
  401. // and links in the formatter
  402. $clause = 'The ' . $subject_type . ', ' .
  403. $subject_name . ', ' . $verb . ' ' . $rel_type_clean . ' ' .
  404. $object_type . ', ' . $object_name . '.';
  405. $entity->{$field_name}['und'][$delta]['value']['SIO:000493'] = $clause;
  406. // Adding a label allows us to provide a single text value for the
  407. // entire field. It is this text value that can be used in tab/csv
  408. // downloaders.
  409. $entity->{$field_name}['und'][$delta]['value']['rdfs:label'] = $clause;
  410. $entity->{$field_name}['und'][$delta]['chado-' . $field_table . '__' . $pkey] = $relationship->$pkey;
  411. $entity->{$field_name}['und'][$delta]['chado-' . $field_table . '__' . $subject_id_key] = $relationship->$subject_id_key->$subject_pkey;
  412. $entity->{$field_name}['und'][$delta]['chado-' . $field_table . '__type_id'] = $relationship->type_id->cvterm_id;
  413. $entity->{$field_name}['und'][$delta]['chado-' . $field_table . '__' . $object_id_key] = $relationship->$object_id_key->$object_pkey;
  414. // For the widget to work properly we will preform values.
  415. $entity->{$field_name}['und'][$delta]['type_name'] = $relationship->type_id->name;
  416. $entity->{$field_name}['und'][$delta]['subject_name'] = $subject_name . ' [id: ' . $relationship->$subject_id_key->$subject_pkey . ']';
  417. $entity->{$field_name}['und'][$delta]['object_name'] = $object_name . ' [id: ' . $relationship->$object_id_key->$object_pkey . ']';
  418. if (array_key_exists('value', $this->schema['fields'])) {
  419. $entity->{$field_name}['und'][$delta]['chado-' . $field_table . '__value'] = $relationship->value;
  420. }
  421. if (array_key_exists('rank', $this->schema['fields'])) {
  422. $entity->{$field_name}['und'][$delta]['chado-' . $field_table . '__rank'] = $relationship->rank;
  423. }
  424. }
  425. /**
  426. *
  427. * @see TripalField::load()
  428. */
  429. public function load($entity) {
  430. $settings = $this->field['settings'];
  431. $record = $entity->chado_record;
  432. $field_name = $this->field['field_name'];
  433. $field_type = $this->field['type'];
  434. $field_table = $this->instance['settings']['chado_table'];
  435. $field_column = $this->instance['settings']['chado_column'];
  436. $base_table = $this->instance['settings']['base_table'];
  437. $rel_table = $field_table;
  438. // Get the PKey for this table
  439. $pkey = $this->schema['primary key'][0];
  440. // Not all tables have the columns named 'subject_id' and 'object_id'.
  441. // some have variations on that name and we need to determine what they are.
  442. $subject_id_key = $this->subject_id_column;
  443. $object_id_key = $this->object_id_column;
  444. // If we don't have a chado record return before creating a stub for this field!
  445. if (!$record) {
  446. return;
  447. }
  448. // Set some defaults for the empty record.
  449. $entity->{$field_name}['und'][0] = [
  450. 'value' => '',
  451. 'chado-' . $field_table . '__' . $pkey => '',
  452. 'chado-' . $field_table . '__' . $subject_id_key => '',
  453. 'chado-' . $field_table . '__' . $object_id_key => '',
  454. 'chado-' . $field_table . '__type_id' => '',
  455. // These elements don't need to follow the naming scheme above
  456. // because we don't need the chado_field_storage to try and
  457. // save these values.
  458. 'object_name' => '',
  459. 'subject_name' => '',
  460. 'type_name' => '',
  461. ];
  462. // If the table has rank and value fields then add those to the default
  463. // value array.
  464. if (array_key_exists('value', $this->schema['fields'])) {
  465. $entity->{$field_name}['und'][0]['chado-' . $field_table . '__value'] = '';
  466. }
  467. if (array_key_exists('rank', $this->schema['fields'])) {
  468. $entity->{$field_name}['und'][0]['chado-' . $field_table . '__rank'] = '';
  469. }
  470. // Expand the object to include the relationships.
  471. $options = [
  472. 'return_array' => 1,
  473. // we don't want to fully recurse we only need information about the
  474. // relationship type and the object and subject
  475. 'include_fk' => [
  476. 'type_id' => 1,
  477. $object_id_key => [
  478. 'type_id' => 1,
  479. 'organism_id' => 1,
  480. ],
  481. $subject_id_key => [
  482. 'type_id' => 1,
  483. 'organism_id' => 1,
  484. ],
  485. ],
  486. ];
  487. if (array_key_exists('rank', $this->schema['fields'])) {
  488. $options['order_by'] = ['rank' => 'ASC'];
  489. }
  490. $record = chado_expand_var($record, 'table', $rel_table, $options);
  491. if (!$record->$rel_table) {
  492. return;
  493. }
  494. // Load the subject relationships
  495. $i = 0;
  496. if (isset($record->$rel_table->$subject_id_key)) {
  497. $srelationships = $record->$rel_table->$subject_id_key;
  498. foreach ($srelationships as $relationship) {
  499. $this->loadRelationship($relationship, $entity, $i);
  500. $i++;
  501. }
  502. }
  503. // Load the object relationships
  504. if (isset($record->$rel_table->$object_id_key)) {
  505. $orelationships = $record->$rel_table->$object_id_key;
  506. foreach ($orelationships as $relationship) {
  507. $this->loadRelationship($relationship, $entity, $i);
  508. $i++;
  509. }
  510. }
  511. }
  512. /**
  513. * @see ChadoField::query()
  514. */
  515. public function query($query, $condition) {
  516. $alias = $this->field['field_name'];
  517. $chado_table = $this->instance['settings']['chado_table'];
  518. $base_table = $this->instance['settings']['base_table'];
  519. $bschema = chado_get_schema($base_table);
  520. $bpkey = $bschema['primary key'][0];
  521. $operator = $condition['operator'];
  522. // Bulid the list of expected elements that will be provided.
  523. $field_term_id = $this->getFieldTermID();
  524. $rel_subject = $field_term_id . ',local:relationship_subject';
  525. $rel_subject_type = $rel_subject . ',' . 'rdfs:type';
  526. $rel_subject_name = $rel_subject . ',' . 'schema:name';
  527. $rel_subject_identifier = $rel_subject . ',' . 'data:0842';
  528. $rel_type = $field_term_id . ',local:relationship_type';
  529. $rel_object = $field_term_id . ',local:relationship_object';
  530. $rel_object_type = $rel_object . ',' . 'rdfs:type';
  531. $rel_object_name = $rel_object . ',' . 'schema:name';
  532. $rel_object_identifier = $rel_object . ',' . 'data:0842';
  533. // Filter by the name of the subject or object.
  534. if ($condition['column'] == $rel_subject_name) {
  535. $this->queryJoinOnce($query, $chado_table, $alias, "base.$bpkey = $alias.object_id");
  536. $this->queryJoinOnce($query, $base_table, 'base2', "base2.$bpkey = $alias.subject_id");
  537. $query->condition("base2.name", $condition['value'], $operator);
  538. }
  539. if ($condition['column'] == $rel_object_name) {
  540. $this->queryJoinOnce($query, $chado_table, $alias, "base.$bpkey = $alias.subject_id");
  541. $this->queryJoinOnce($query, $base_table, 'base2', "base2.$bpkey = $alias.object_id");
  542. $query->condition("base2.name", $condition['value'], $operator);
  543. }
  544. // Filter by unique name of the subject or object.
  545. // If this table has a uniquename!
  546. if (isset($this->schema['fields']['uniquename'])) {
  547. if ($condition['column'] == $rel_subject_identifier) {
  548. $this->queryJoinOnce($query, $chado_table, $alias, "base.$bpkey = $alias.object_id");
  549. $this->queryJoinOnce($query, $base_table, 'base2', "base2.$bpkey = $alias.subject_id");
  550. $query->condition("base2.uniquename", $condition['value'], $operator);
  551. }
  552. if ($condition['column'] == $rel_object_identifier) {
  553. $this->queryJoinOnce($query, $chado_table, $alias, "base.$bpkey = $alias.subject_id");
  554. $this->queryJoinOnce($query, $base_table, 'base2', "base2.$bpkey = $alias.object_id");
  555. $query->condition("base2.uniquename", $condition['value'], $operator);
  556. }
  557. }
  558. // Filter by the type of the subject or object
  559. if ($condition['column'] == $rel_subject_type) {
  560. $this->queryJoinOnce($query, $chado_table, $alias, "base.$bpkey = $alias.object_id");
  561. $this->queryJoinOnce($query, $base_table, 'base2', "base2.$bpkey = $alias.subject_id");
  562. $this->queryJoinOnce($query, 'cvterm', 'SubjectCVT', "SubjectCVT.cvterm_id = base2.type_id");
  563. $this->queryJoinOnce($query, "SubjectCVT.name", $condition['value'], $operator);
  564. }
  565. if ($condition['column'] == $rel_object_type) {
  566. $this->queryJoinOnce($query, $chado_table, $alias, "base.$bpkey = $alias.subject_id");
  567. $this->queryJoinOnce($query, $base_table, 'base2', "base2.$bpkey = $alias.object_id");
  568. $this->queryJoinOnce($query, 'cvterm', 'ObjectCVT', "ObjectCVT.cvterm_id = base2.type_id");
  569. $query->condition("ObjectCVT.name", $condition['value'], $operator);
  570. }
  571. // Filter by relationship type
  572. if ($condition['column'] == 'relationship.relationship_type') {
  573. // This filter commented out because it's way to slow...
  574. // $this->queryJoinOnce($query, $chado_table, $alias, "base.$bpkey = $alias.subject_id OR base.$bpkey = $alias.object_id");
  575. // $this->queryJoinOnce($query, 'cvterm', 'RelTypeCVT', "RelTypeCVT.cvterm_id = $alias.type_id");
  576. // $query->condition("RelTypeCVT.name", $condition['value'], $operator);
  577. }
  578. }
  579. /**
  580. * A helper function to define English verbs for relationship types.
  581. *
  582. * @param $rel_type
  583. * The vocabulary term name for the relationship.
  584. *
  585. * @return
  586. * The verb to use when creating a sentence of the relationship.
  587. */
  588. public static function get_rel_verb($rel_type) {
  589. $rel_type_clean = lcfirst(preg_replace('/_/', ' ', $rel_type));
  590. $verb = '';
  591. switch ($rel_type_clean) {
  592. case 'integral part of':
  593. case 'instance of':
  594. $verb = 'is an';
  595. break;
  596. case 'proper part of':
  597. case 'transformation of':
  598. case 'genome of':
  599. case 'part of':
  600. $verb = 'is a';
  601. break;
  602. case 'position of':
  603. case 'sequence of':
  604. case 'variant of':
  605. $verb = 'is a';
  606. break;
  607. case 'derives from':
  608. case 'connects on':
  609. case 'contains':
  610. case 'finishes':
  611. case 'guides':
  612. case 'has origin':
  613. case 'has part':
  614. case 'has quality':
  615. case 'is a':
  616. case 'is a maternal parent of':
  617. case 'is a paternal parent of':
  618. case 'is a subproject of':
  619. case 'is consecutive sequence of':
  620. case 'maximally overlaps':
  621. case 'overlaps':
  622. case 'starts':
  623. break;
  624. default:
  625. $verb = 'is';
  626. }
  627. return $verb;
  628. }
  629. /**
  630. *
  631. * @see TripalField::settingsForm()
  632. */
  633. public function instanceSettingsForm() {
  634. $element = parent::instanceSettingsForm();
  635. $element['items_per_page'] = [
  636. '#type' => 'textfield',
  637. '#title' => 'Items per Page',
  638. '#description' => t('The number of items that should appear on each page. A pager is provided if more than this number of items exist.'),
  639. '#default_value' => $this->instance['settings']['items_per_page'],
  640. ];
  641. //$element = parent::instanceSettingsForm();
  642. $element['relationships'] = [
  643. '#type' => 'fieldset',
  644. '#title' => 'Allowed Relationship Types',
  645. '#description' => t('There are three ways that relationship types
  646. can be limited for users who have permission to add new relationships.
  647. Please select the most appropriate for you use case. By default
  648. all vocabularies are provided to the user which allows use of any
  649. term for the relationship type.'),
  650. '#collapsed' => TRUE,
  651. '#collapsible' => TRUE,
  652. ];
  653. // $element['instructions'] = array(
  654. // '#type' => 'item',
  655. // '#markup' => 'You may provide a list of terms that will be available in a select box
  656. // as the relationship types. This select box will replace the vocabulary select box if the
  657. // following value is set.'
  658. // );
  659. $vocs = chado_get_cv_select_options();
  660. $element['relationships']['option1'] = [
  661. '#type' => 'item',
  662. '#title' => 'Option #1',
  663. '#description' => t('Use this option to limit the vocabularies that a user .
  664. could use to specify relationship types. With this option any term in .
  665. the vocabulary can be used for the relationship type. You may select
  666. more than one vocabulary.'),
  667. ];
  668. $element['relationships']['option1_vocabs'] = [
  669. '#type' => 'select',
  670. '#multiple' => TRUE,
  671. '#options' => $vocs,
  672. '#size' => 6,
  673. '#default_value' => $this->instance['settings']['relationships']['option1_vocabs'],
  674. // TODO add ajax here so that the relationship autocomplete below works
  675. ];
  676. $element['relationships']['option2'] = [
  677. '#type' => 'item',
  678. '#title' => '<b>Option #2</b>',
  679. '#description' => 'Some vocabularies are heirarchichal (an ontology). Within this
  680. heirarchy groups of related terms typically fall under a common parent. If you
  681. wish to limit the list of terms that a user can use for the relationship type,
  682. you can provide the parent term here. Then, only that term\'s children will
  683. be avilable for use as a relationship type.',
  684. ];
  685. $element['relationships']['option2_vocab'] = [
  686. '#type' => 'select',
  687. '#description' => 'Specify Default Vocabulary',
  688. '#multiple' => FALSE,
  689. '#options' => $vocs,
  690. '#default_value' => $this->instance['settings']['relationships']['option2_vocab'],
  691. '#ajax' => [
  692. 'callback' => "sbo__relationship_instance_settings_form_ajax_callback",
  693. 'wrapper' => 'relationships-option2-parent',
  694. 'effect' => 'fade',
  695. 'method' => 'replace',
  696. ],
  697. ];
  698. $element['relationships']['option2_parent'] = [
  699. '#type' => 'textfield',
  700. '#description' => 'Specify a Heirarchical Parent Term',
  701. '#default_value' => $this->instance['settings']['relationships']['option2_parent'],
  702. '#autocomplete_path' => "admin/tripal/storage/chado/auto_name/cvterm/",
  703. '#prefix' => '<div id=relationships-option2-parent>',
  704. '#suffix' => '</div>',
  705. ];
  706. $element['relationships']['option3'] = [
  707. '#type' => 'item',
  708. '#title' => 'Option #3',
  709. '#description' => 'Provide terms separated by a new line. The term provided should be
  710. unique and distinguishable by the name. You can use a bar | to separate a vocabulary
  711. and a term to allow more specific assignment.',
  712. ];
  713. $element['relationships']['relationship_types'] = [
  714. '#type' => 'textarea',
  715. '#default_value' => $this->instance['settings']['relationships']['relationship_types'],
  716. ];
  717. return $element;
  718. }
  719. /**
  720. *
  721. * @param unknown $form
  722. * @param unknown $form_state
  723. */
  724. public function instanceSettingsFormValidate($form, &$form_state) {
  725. // Get relationships settings
  726. $settings = $form_state['values']['instance']['settings']['relationships'];
  727. $form_state['values']['instance']['settings']['relationships']['relationship_types'] = trim($settings['relationship_types']);
  728. // Make sure only one option is selected
  729. $option1test = $settings['option1_vocabs'];
  730. $option1 = isset($settings['option1_vocabs']) && array_pop($option1test);
  731. $option2 = (isset($settings['option2_vocab']) && $settings['option2_vocab']) || $settings['option2_parent'];
  732. $option3 = isset($settings['relationship_types']) && trim($settings['relationship_types']);
  733. if ($option1 && ($option2 || $option3) == 1 ||
  734. $option2 && ($option1 || $option3) == 1 ||
  735. $option3 && ($option1 || $option2) == 1) {
  736. form_set_error("instance][settings][relationships", t("Only one option is allowed to limit the relationship types."));
  737. return;
  738. }
  739. // For option3, make sure the supplied types are valid cvterms
  740. if ($option3) {
  741. $rel_types = explode(PHP_EOL, $settings['relationship_types']);
  742. foreach ($rel_types AS $type) {
  743. $type = trim($type);
  744. // Ignore empty lines
  745. if ($type == '') {
  746. continue;
  747. }
  748. // Find the matching cvterm
  749. $sql = "SELECT cvterm_id FROM {cvterm} WHERE name = :name";
  750. $results = chado_query($sql, [':name' => $type]);
  751. $terms = [];
  752. while ($obj = $results->fetchObject()) {
  753. $terms[] = $obj;
  754. }
  755. // Don't save the form if a term can not be found or it matches more than one cvterm
  756. $cv = '';
  757. if (count($terms) == 0) {
  758. // If a term can not be found, maybe the type contains '|', parse it as 'vocabulary|cvterm'
  759. if (strpos($type, '|')) {
  760. $tmp = explode('|', $type, 2);
  761. $type = trim($tmp[1]);
  762. $cv = chado_get_cv(['name' => trim($tmp[0])]);
  763. if ($cv) {
  764. $sql = "SELECT cvterm_id FROM {cvterm} WHERE name = :name AND cv_id = :cv_id";
  765. $results = chado_query($sql, [
  766. ':name' => $type,
  767. ':cv_id' => $cv->cv_id,
  768. ]);
  769. while ($obj = $results->fetchObject()) {
  770. $terms[] = $obj;
  771. }
  772. }
  773. else {
  774. $cv = $tmp[0];
  775. }
  776. }
  777. if (count($terms) != 1) {
  778. $message = "The term '@type' can not be found.";
  779. $token = ['@type' => $type];
  780. if ($cv) {
  781. $message = "The term '@type' can not be found within the vocabulary '@vocab'.";
  782. $token['@vocab'] = $cv;
  783. }
  784. form_set_error("instance][settings][relationships][relationship_types",
  785. t($message, $token));
  786. }
  787. }
  788. else {
  789. if (count($terms) > 1) {
  790. // If a type matches more than one term, parse it as 'vocabulary|cvterm' and try again
  791. if (strpos($type, '|')) {
  792. $tmp = explode('|', $type, 2);
  793. $type = trim($tmp[1]);
  794. $cv = chado_get_cv(['name' => trim($tmp[0])]);
  795. if ($cv) {
  796. $sql = "SELECT cvterm_id FROM {cvterm} WHERE name = :name AND cv_id = :cv_id";
  797. $results = chado_query($sql, [
  798. ':name' => $type,
  799. ':cv_id' => $cv->cv_id,
  800. ]);
  801. while ($obj = $results->fetchObject()) {
  802. $terms[] = $obj;
  803. }
  804. }
  805. }
  806. if (count($terms) != 1) {
  807. form_set_error("instance][settings][relationships][relationship_types",
  808. t("The term '@type' matches more than one term. Please specify its vocabulary in the format of 'vocabulary|@type'.", ['@type' => $type]));
  809. }
  810. }
  811. }
  812. }
  813. }
  814. // For option2: Make sure the parent term is a valid cvterm
  815. if ($option2) {
  816. $cv_id = $settings['option2_vocab'];
  817. $supertype = $settings['option2_parent'];
  818. $term = chado_get_cvterm([
  819. 'name' => trim($supertype),
  820. 'cv_id' => $cv_id,
  821. ]);
  822. // Tripal cv autocomplete also allow cvterm synonyms, if the parent term doesn't match
  823. // a cvterm, try cvtermsynonym
  824. if (!$term) {
  825. $synonym = chado_get_cvterm(
  826. [
  827. 'synonym' => [
  828. 'name' => trim($supertype),
  829. ],
  830. ]
  831. );
  832. if ($synonym && $synonym->cv_id->cv_id == $cv_id) {
  833. $term = $synonym;
  834. }
  835. }
  836. if (!isset($term->cvterm_id)) {
  837. form_set_error("instance][settings][relationships][option2_parent",
  838. t("The term '@type' is not a valid term for the vocabulary selected.", ['@type' => $supertype]));
  839. }
  840. }
  841. }
  842. /**
  843. * @see TripalField::validate()
  844. */
  845. public function validate($entity_type, $entity, $langcode, $items, &$errors) {
  846. // If we don't have an entity then we don't want to validate. The case
  847. // where this could happen is when a user is editing the field settings
  848. // and trying to set a default value. In that case there's no entity and
  849. // we don't want to validate. There will always be an entity for creation
  850. // and update operations of a content type.
  851. if (!$entity) {
  852. return;
  853. }
  854. $field_name = $this->field['field_name'];
  855. $field_type = $this->field['type'];
  856. $field_table = $this->instance['settings']['chado_table'];
  857. $field_column = $this->instance['settings']['chado_column'];
  858. $base_table = $this->instance['settings']['base_table'];
  859. // Grab the chado record_id for this entity.
  860. $chado_record_id = NULL;
  861. if ($entity AND isset($entity->chado_record_id)) {
  862. $chado_record_id = $entity->chado_record_id;
  863. }
  864. // Validate each releationship.
  865. foreach ($items as $delta => $item) {
  866. $item_errors = $this->validateItem($item, $chado_record_id);
  867. if (!empty($item_errors)) {
  868. $errors[$field_name][$delta][$langcode] = $item_errors;
  869. }
  870. }
  871. }
  872. /**
  873. * Validate a Single relationship.
  874. *
  875. * @param $item
  876. * A single item from the $items array passed to TripalField::validate().
  877. *
  878. * @return
  879. * An array of errors where each has a:
  880. * - error: this is an error code which is the name of the field.
  881. * - message: A message to show the user describing the problem.
  882. */
  883. public function validateItem($item, $chado_record_id = NULL) {
  884. $errors = [];
  885. $field_name = $this->field['field_name'];
  886. $field_type = $this->field['type'];
  887. $field_table = $this->instance['settings']['chado_table'];
  888. $field_column = $this->instance['settings']['chado_column'];
  889. $base_table = $this->instance['settings']['base_table'];
  890. // 'nd_reagent_relationship' and 'project_relationship' have different column names from
  891. // subject_id/object_id. Do a pattern matching to get the column names.
  892. $subject_id_key = $this->subject_id_column;
  893. $object_id_key = $this->object_id_column;
  894. $subject_id = $item['chado-' . $field_table . '__' . $subject_id_key];
  895. $object_id = $item['chado-' . $field_table . '__' . $object_id_key];
  896. $type_id = $item['chado-' . $field_table . '__type_id'];
  897. $type_id = isset($item['type_id']) ? $item['chado-' . $field_table . '__type_id'] : $type_id;
  898. $type_name = isset($item['type_name']) ? $item['type_name'] : '';
  899. $voc_id = isset($item['vocabulary']) ? $item['vocabulary'] : '';
  900. $subject_name = isset($item['subject_name']) ? $item['subject_name'] : '';
  901. $object_name = isset($item['object_name']) ? $item['object_name'] : '';
  902. // If the row is empty then just continue, there's nothing to validate.
  903. if (!$type_id and !$type_name and !$subject_name and !$object_name) {
  904. return;
  905. }
  906. // Check: Make sure we have values for all of the fields.
  907. if (!$type_name && !$type_id) {
  908. $errors[] = [
  909. 'error' => 'sbo__relationship',
  910. 'message' => t("Please provide the type of relationship."),
  911. 'element' => 'type',
  912. ];
  913. }
  914. if (!$subject_name) {
  915. $errors[] = [
  916. 'error' => 'sbo__relationship',
  917. 'message' => t("Please provide the subject of the relationship."),
  918. 'element' => 'subject',
  919. ];
  920. }
  921. if (!$object_name) {
  922. $errors[] = [
  923. 'error' => 'sbo__relationship',
  924. 'message' => t("Please provide the object of the relationship."),
  925. 'element' => 'object',
  926. ];
  927. }
  928. // Check: Cvterm exists.
  929. if (!$type_id AND !$type_name) {
  930. $errors[] = [
  931. 'error' => 'sbo__relationship',
  932. 'message' => t("We were unable to find the type you specified. Please check spelling and that the term already exists."),
  933. 'element' => 'type',
  934. ];
  935. }
  936. elseif ($type_name AND $voc_id) {
  937. $val = [
  938. 'cv_id' => $voc_id,
  939. 'name' => $type_name,
  940. ];
  941. $cvterm = chado_generate_var('cvterm', $val);
  942. if (!isset($cvterm->cvterm_id)) {
  943. $errors[] = [
  944. 'error' => 'sbo__relationship',
  945. 'message' => t("We were unable to find the type you specified. Please check spelling and that the term already exists."),
  946. 'element' => 'type',
  947. ];
  948. }
  949. }
  950. // Before submitting this form we need to make sure that our subject_id and
  951. // object_ids are real records. There are two ways to get the record, either
  952. // just with the text value or with an [id: \d+] string embedded. If the
  953. // later we will pull it out.
  954. $subject_id = '';
  955. $fkey_rcolumn = $this->schema['foreign keys'][$base_table]['columns'][$subject_id_key];
  956. $matches = [];
  957. if (preg_match('/\[id: (\d+)\]/', $subject_name, $matches)) {
  958. $subject_id = $matches[1];
  959. $values = [$fkey_rcolumn => $subject_id];
  960. $subject = chado_select_record($base_table, [$fkey_rcolumn], $values);
  961. if (count($subject) == 0) {
  962. $errors[] = [
  963. 'error' => 'sbo__relationship',
  964. 'message' => t("The subject record cannot be found using the specified id (e.g. [id: xx])."),
  965. 'element' => 'subject',
  966. ];
  967. }
  968. }
  969. else {
  970. // Otherwise we need to look it up using the name field determined in the
  971. // constructor for the current field. There may be more then one name field
  972. // (e.g. organism: genus + species) so we want to check both.
  973. $sql = 'SELECT ' . $fkey_rcolumn . ' FROM {' . $base_table . '} WHERE ' . implode('||', $this->base_name_columns) . '=:keyword';
  974. $subject = chado_query($sql, [':keyword' => $subject_name])->fetchAll();
  975. if (count($subject) == 0 AND $chado_record_id) {
  976. $errors[] = [
  977. 'error' => 'sbo__relationship',
  978. 'message' => t("The subject record cannot be found. Please check spelling."),
  979. 'element' => 'subject',
  980. ];
  981. }
  982. elseif (count($subject) > 1) {
  983. $errors[] = [
  984. 'error' => 'sbo__relationship',
  985. 'message' => t("The subject is not unique and therefore the relationship cannot be made."),
  986. 'element' => 'subject',
  987. ];
  988. }
  989. }
  990. // Now check for a matching object.
  991. $object_id = '';
  992. $fkey_rcolumn = $this->schema['foreign keys'][$base_table]['columns'][$object_id_key];
  993. $matches = [];
  994. if (preg_match('/\[id: (\d+)\]/', $object_name, $matches)) {
  995. $object_id = $matches[1];
  996. $values = [$fkey_rcolumn => $object_id];
  997. $object = chado_select_record($base_table, [$fkey_rcolumn], $values);
  998. if (count($subject) == 0 AND $chado_record_id) {
  999. $errors[] = [
  1000. 'error' => 'sbo__relationship',
  1001. 'message' => t("The object record cannot be found using the specified id (e.g. [id: xx])."),
  1002. 'element' => 'object',
  1003. ];
  1004. }
  1005. }
  1006. else {
  1007. // Otherwise we need to look it up using the name field determined in the
  1008. // constructor for the current field. There may be more then one name field
  1009. // (e.g. organism: genus + species) so we want to check both.
  1010. $sql = 'SELECT ' . $fkey_rcolumn . ' FROM {' . $base_table . '} WHERE ' . implode('||', $this->base_name_columns) . '=:keyword';
  1011. $object = chado_query($sql, [':keyword' => $object_name]);
  1012. if (count($object) == 0) {
  1013. $errors[] = [
  1014. 'error' => 'sbo__relationship',
  1015. 'message' => t("The object record cannot be found. Please check spelling."),
  1016. 'element' => 'object',
  1017. ];
  1018. }
  1019. elseif (count($object) > 1) {
  1020. $errors[] = [
  1021. 'error' => 'sbo__relationship',
  1022. 'message' => t("The object is not unique and therefore the relationship cannot be made."),
  1023. 'element' => 'object',
  1024. ];
  1025. }
  1026. }
  1027. // Make sure that either our object or our subject refers to the base record.
  1028. if ($object_id AND $subject_id) {
  1029. if ($object_id != $chado_record_id and $subject_id != $chado_record_id) {
  1030. $errors[] = [
  1031. 'error' => 'sbo__relationship',
  1032. 'message' => t("Either the subject or the object in the relationship must refer to this record."),
  1033. 'element' => 'row',
  1034. ];
  1035. }
  1036. }
  1037. // Make sure that the object and subject are not both the same thing.
  1038. if ($object_id AND $subject_id) {
  1039. if ($object_id == $subject_id) {
  1040. $errors[] = [
  1041. 'error' => 'sbo__relationship',
  1042. 'message' => t("The subject and the object in the relationship cannot both refer to the same record."),
  1043. 'element' => 'row',
  1044. ];
  1045. }
  1046. }
  1047. return $errors;
  1048. }
  1049. /**
  1050. * @see ChadoField::queryOrder()
  1051. */
  1052. public function queryOrder($query, $order) {
  1053. }
  1054. /**
  1055. * Retrieve the schema's
  1056. */
  1057. public function getRelTableSchema() {
  1058. return $this->schema;
  1059. }
  1060. public function getBaseTableSchema() {
  1061. return $this->base_schema;
  1062. }
  1063. /**
  1064. * Retrieve the subject/object key columns.
  1065. */
  1066. public function getSubjectIdColumn() {
  1067. return $this->subject_id_column;
  1068. }
  1069. public function getObjectIdColumn() {
  1070. return $this->object_id_column;
  1071. }
  1072. /**
  1073. * Retrieve the base name columns.
  1074. */
  1075. public function getBaseNameColumns() {
  1076. return $this->base_name_columns;
  1077. }
  1078. /**
  1079. * Retrieve the base type column.
  1080. */
  1081. public function getBaseTypeColumn() {
  1082. return $this->base_type_column;
  1083. }
  1084. }