views_handler_join_chado_aggregator.inc 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. <?php
  2. /**
  3. * @file
  4. * Handler to allow joins between records via a linking table
  5. *
  6. * Example Usage:
  7. * To join the analysisprop table to the analysis table,
  8. * Use the following code in the analysisprop hook_views_data:
  9. * @code
  10. $data['analysisprop']['table']['join']['analysis'] = array(
  11. 'left_field' => 'analysis_id',
  12. 'field' => 'analysis_id',
  13. 'handler' => 'views_handler_join_chado_aggregator'
  14. );
  15. * @endcode
  16. */
  17. class views_handler_join_chado_aggregator extends views_join {
  18. // PHP 4 doesn't call constructors of the base class automatically from a
  19. // constructor of a derived class. It is your responsibility to propagate
  20. // the call to constructors upstream where appropriate.
  21. function construct($table = NULL, $left_table = NULL, $left_field = NULL, $field = NULL, $extra = array(), $type = 'LEFT', $added = NULL) {
  22. parent::construct($table, $left_table, $left_field, $field, $extra, $type);
  23. // Determine the postgresql version
  24. $postgresql_version = pg_version();
  25. $this->postgresql_version = $postgresql_version['client'];
  26. // If version is 9.0+ then indicate
  27. // Needed to apply sorting for aggregated fields
  28. if (intval($postgresql_version['client']) >= 9) {
  29. $this->postgresql_9up = TRUE;
  30. }
  31. }
  32. /**
  33. * Creates SQL including aggregation query used in join
  34. */
  35. function join($table, &$query) {
  36. $output = array();
  37. // Create the table SQL (used in join) -------
  38. // query creating one-to-one table using array_agg
  39. $table_desc = module_invoke_all('chado_' . $this->definition['table'] . '_schema');
  40. $select_fields[ $this->definition['table'] ] = $table_desc['fields'];
  41. // Add joins to tables with a foreign key in this table
  42. // (ie: add join to cvterm if this table has a type_id
  43. $joins = array();
  44. foreach ($table_desc['foreign keys'] as $defn) {
  45. if ($defn['table'] != $this->left_table) {
  46. foreach ( $defn['columns'] as $left => $right) {
  47. $left = $this->definition['table'] .'.'. $left;
  48. $right = $defn['table'] .'.'. $right;
  49. $joins[] = "LEFT JOIN $defn[table] $defn[table] ON $left=$right";
  50. }
  51. // Fields to be selected from joined table
  52. $join_table = module_invoke_all('chado_' . $defn['table'] . '_schema');
  53. $select_fields[ $defn['table'] ] = $join_table['fields'];
  54. }
  55. }
  56. // Determine Order BY's for aggregates
  57. $order_by = array();
  58. foreach ($this->sort as $s) {
  59. $order_by[] = $s['table'] . '.' . $s['field'] . ' ' . $s['order'];
  60. }
  61. // Fields to be selected
  62. foreach ($select_fields as $table => $table_fields) {
  63. foreach ($table_fields as $fname => $f) {
  64. $alias = '';
  65. if ($table != $this->definition['table']) {
  66. $alias = $table .'_';
  67. }
  68. if ($fname != $this->definition['field']) {
  69. // Add sort to aggregate field if postgreSQL 9.0+
  70. if ($this->postgresql_9up && !empty($order_by)) {
  71. $fields[] = 'array_agg(' . $table . '.' . $fname . ' ORDER BY ' . implode(',', $order_by) . ') as ' . $alias . $fname;
  72. }
  73. else {
  74. $fields[] = 'array_agg(' . $table . '.' . $fname . ') as ' . $alias . $fname;
  75. }
  76. $composite_field_parts[] = "'" . $alias . $fname . "::' ||" . $table . '.' . $fname;
  77. }
  78. else {
  79. $fields[] = $fname;
  80. $composite_field_parts[] = "'" . $alias . $fname . "::' ||" . $table . '.' . $fname;
  81. }
  82. }
  83. }
  84. // composite field
  85. // (combines all other fields before aggregating)
  86. // Add sort to aggregate field if postgreSQL 9.0+
  87. if ($this->postgresql_9up && !empty($order_by)) {
  88. $composite_field = "array_agg('{'||" . implode(" || ',' || ", $composite_field_parts) . "||'}' ORDER BY " . implode(',', $order_by) . ") as all";
  89. }
  90. else {
  91. $composite_field = "array_agg('{'||" . implode(" || ',' || ", $composite_field_parts) . "||'}') as all";
  92. }
  93. $fields[] = $composite_field;
  94. // SQL to use in the join
  95. $sql = 'SELECT ' . implode(', ', $fields)
  96. .' FROM ' . $this->definition['table']
  97. .' ' . implode(' ', $joins);
  98. if (!empty($this->filter)) {
  99. $sql .= ' WHERE ' . implode(', ', $this->filter);
  100. }
  101. $sql .= ' GROUP BY ' . $this->definition['field'];
  102. // Create the join (full SQL) ----------------
  103. $output[] = $this->create_single_join(
  104. $query,
  105. array(
  106. 'table' => $this->definition['table'],
  107. 'field' => $this->definition['field'],
  108. 'table_sql' => $sql,
  109. 'is_drupal' => FALSE,
  110. ),
  111. array(
  112. 'table' => $this->definition['left_table'],
  113. 'field' => $this->definition['left_field'],
  114. ),
  115. 'LEFT'
  116. );
  117. return implode("\n", $output);
  118. }
  119. /**
  120. * Creates SQL for a single join based on parameters
  121. * Join will be: <type> JOIN (<query creating one-to-one table using array_agg>) <table alias>
  122. * ON <qualified left field>=<qualified right field>
  123. */
  124. function create_single_join(&$query, $right_spec, $left_spec, $join_type) {
  125. if ($right_spec['table']) {
  126. $right = $query->get_table_info($right_spec['table']);
  127. if (!$right['alias']) {
  128. $right['alias'] = $right_spec['table'];
  129. }
  130. $right_field = "$right[alias].$right_spec[field]";
  131. if ($right_spec['is_drupal']) {
  132. $right_table = '{' . $right_spec['table'] . '}';
  133. }
  134. else {
  135. $right_table = $right_spec['table'];
  136. }
  137. }
  138. if ($left_spec['table']) {
  139. $left = $query->get_table_info($left_spec['table']);
  140. if (!$left['alias']) {
  141. $left['alias'] = $left_spec['table'];
  142. }
  143. $left_field = "$left[alias].$left_spec[field]";
  144. }
  145. else {
  146. // This can be used if left_field is a formula or something. It should be used only *very* rarely.
  147. $left_field = $this->left_spec['field'];
  148. }
  149. $output = " $join_type JOIN ($right_spec[table_sql]) $right[alias] ON $left_field = $right_field";
  150. return $output;
  151. }
  152. }