franck@0
|
1 <?php |
franck@0
|
2 // $Id: views_calc_plugin_style_chart.inc,v 1.7 2009/04/20 19:38:42 karens Exp $ |
franck@0
|
3 |
franck@0
|
4 /** |
franck@0
|
5 * @file |
franck@0
|
6 * Contains the chart style plugin. |
franck@0
|
7 */ |
franck@0
|
8 |
franck@0
|
9 /** |
franck@0
|
10 * Style plugin to render view as a chart. |
franck@0
|
11 * |
franck@0
|
12 * @ingroup views_style_plugins |
franck@0
|
13 */ |
franck@0
|
14 class views_calc_plugin_style_chart extends views_plugin_style { |
franck@0
|
15 // Set default options. |
franck@0
|
16 function options(&$options) { |
franck@0
|
17 $options['format'] = 'pie2D'; |
franck@0
|
18 $options['height'] = 200; |
franck@0
|
19 $options['width'] = 400; |
franck@0
|
20 $options['color'] = 'ffffff'; |
franck@0
|
21 $options['aggregation_field'] = ''; |
franck@0
|
22 $options['calc_fields'] = array(); |
franck@0
|
23 $options['calc'] = 'COUNT'; |
franck@0
|
24 $options['precision'] = 2; |
franck@0
|
25 } |
franck@0
|
26 |
franck@0
|
27 // Generate a form for setting options. |
franck@0
|
28 function options_form(&$form, &$form_state) { |
franck@0
|
29 parent::options_form($form, $form_state); |
franck@0
|
30 |
franck@0
|
31 $form['format'] = array( |
franck@0
|
32 '#type' => 'select', |
franck@0
|
33 '#title' => t('Chart format'), |
franck@0
|
34 '#options' => array( |
franck@0
|
35 'line2D' => t('Line 2D'), |
franck@0
|
36 'hbar2D' => t('Horizontal Bar 2D'), |
franck@0
|
37 'vbar2D' => t('Vertical Bar 2D'), |
franck@0
|
38 'pie2D' => t('Pie 2D'), |
franck@0
|
39 'pie3D' => t('Pie 3D'), |
franck@0
|
40 'venn' => t('Venn'), |
franck@0
|
41 'scatter' => t('Scatter Plot') |
franck@0
|
42 ), |
franck@0
|
43 '#default_value' => $this->options['format'], |
franck@0
|
44 ); |
franck@0
|
45 $form['height'] = array( |
franck@0
|
46 '#type' => 'textfield', |
franck@0
|
47 '#title' => t('Chart height'), |
franck@0
|
48 '#default_value' => $this->options['height'], |
franck@0
|
49 '#required' => TRUE, // Google charts breaks if it is empty. |
franck@0
|
50 '#description' => t('An integer value, the number of pixels of height for this chart.'), |
franck@0
|
51 ); |
franck@0
|
52 $form['width'] = array( |
franck@0
|
53 '#type' => 'textfield', |
franck@0
|
54 '#title' => t('Chart width'), |
franck@0
|
55 '#default_value' => $this->options['width'], |
franck@0
|
56 '#required' => TRUE, // Google charts breaks if it is empty. |
franck@0
|
57 '#description' => t('An integer value, the number of pixels of width for this chart.'), |
franck@0
|
58 ); |
franck@0
|
59 $form['color'] = array( |
franck@0
|
60 '#type' => 'textfield', |
franck@0
|
61 '#title' => t('Background color'), |
franck@0
|
62 '#default_value' => $this->options['color'], |
franck@0
|
63 '#description' => t('In hexadecimal format (RRGGBB). Do not use the # symbol.'), |
franck@0
|
64 '#required' => TRUE, // Google charts breaks if it is empty. |
franck@0
|
65 ); |
franck@0
|
66 $form['show_legend'] = array( |
franck@0
|
67 '#type' => 'checkbox', |
franck@0
|
68 '#title' => t('Show legend'), |
franck@0
|
69 '#default_value' => $this->options['show_legend'], |
franck@0
|
70 '#description' => t('Display legend next to the chart.'), |
franck@0
|
71 ); |
franck@0
|
72 |
franck@0
|
73 $form['aggregation_field'] = array( |
franck@0
|
74 '#type' => 'select', |
franck@0
|
75 '#title' => t('Aggregation field'), |
franck@0
|
76 '#options' => $this->aggregated_field_options(), |
franck@0
|
77 '#default_value' => $this->options['aggregation_field'], |
franck@0
|
78 '#description' => t('Select a field to aggreagate the results on.') |
franck@0
|
79 ); |
franck@0
|
80 // TODO Charts module cannot currently handle more than one series, |
franck@0
|
81 // update Multiple to TRUE if that changes. |
franck@0
|
82 $form['calc_fields'] = array( |
franck@0
|
83 '#type' => 'select', |
franck@0
|
84 '#title' => t('Computation field'), |
franck@0
|
85 '#options' => $this->aggregated_field_options(), |
franck@0
|
86 '#default_value' => $this->calc_fields(), |
franck@0
|
87 '#multiple' => FALSE, |
franck@0
|
88 '#description' => t('Select field to perform computations on.') |
franck@0
|
89 ); |
franck@0
|
90 $form['calc'] = array( |
franck@0
|
91 '#type' => 'select', |
franck@0
|
92 '#title' => t('Computation to perform'), |
franck@0
|
93 '#options' => $this->calc_options(), |
franck@0
|
94 '#default_value' => $this->options['calc'], |
franck@0
|
95 ); |
franck@0
|
96 $form['precision'] = array( |
franck@0
|
97 '#type' => 'select', |
franck@0
|
98 '#title' => t('Precision'), |
franck@0
|
99 '#options' => range(0, 4), |
franck@0
|
100 '#default_value' => $this->options['precision'], |
franck@0
|
101 '#description' => t('Decimal points to use in computed values.'), |
franck@0
|
102 ); |
franck@0
|
103 } |
franck@0
|
104 |
franck@0
|
105 function calc_options() { |
franck@0
|
106 return array( |
franck@0
|
107 '' => t('None'), |
franck@0
|
108 'SUM' => t('Sum'), |
franck@0
|
109 'COUNT' => t('Count'), |
franck@0
|
110 'AVG' => t('Average'), |
franck@0
|
111 'MIN' => t('Minimum'), |
franck@0
|
112 'MAX' => t('Maximum'), |
franck@0
|
113 ); |
franck@0
|
114 } |
franck@0
|
115 |
franck@0
|
116 /** |
franck@0
|
117 * Create an options array of available fields from this view. |
franck@0
|
118 */ |
franck@0
|
119 function aggregated_field_options() { |
franck@0
|
120 $field_names = array(); |
franck@0
|
121 $handlers = $this->display->handler->get_handlers('field'); |
franck@0
|
122 foreach ($handlers as $field => $handler) { |
franck@0
|
123 if ($label = $handler->label()) { |
franck@0
|
124 $field_names[$field] = $label; |
franck@0
|
125 } |
franck@0
|
126 else { |
franck@0
|
127 $field_names[$field] = $handler->ui_name(); |
franck@0
|
128 } |
franck@0
|
129 } |
franck@0
|
130 return $field_names; |
franck@0
|
131 } |
franck@0
|
132 |
franck@0
|
133 /** |
franck@0
|
134 * Make sure calc_fields is always an array, even when not multiple. |
franck@0
|
135 */ |
franck@0
|
136 function calc_fields() { |
franck@0
|
137 $calc_fields = (array) $this->options['calc_fields']; |
franck@0
|
138 return array_values($calc_fields); |
franck@0
|
139 } |
franck@0
|
140 |
franck@0
|
141 // Define and display a chart from the grouped values. |
franck@0
|
142 function render() { |
franck@0
|
143 // Scan all Views data and insert them into a series. |
franck@0
|
144 $data = array(); |
franck@0
|
145 |
franck@0
|
146 // Get values from rows. |
franck@0
|
147 foreach ($this->calc_fields() as $calc) { |
franck@0
|
148 foreach ($this->view->result as $row) { |
franck@0
|
149 foreach ($this->view->field as $key => $field) { |
franck@0
|
150 if ($key == $this->options['aggregation_field']) { |
franck@0
|
151 $legend_field = array_key_exists($calc, $this->view->field) ? $this->view->field[$calc] : NULL; |
franck@0
|
152 $legend = !empty($legend_field->options['label']) ? $legend_field->options['label'] : NULL; |
franck@0
|
153 if ($this->options['show_legend']) { |
franck@0
|
154 $data[$calc]['#legend'] = $legend; |
franck@0
|
155 } |
franck@0
|
156 $value['#label'] = strip_tags(theme_views_view_field($this->view, $this->view->field[$key], $row)); // .': '. $row->$calc; |
franck@0
|
157 $value['#value'] = $row->$calc; |
franck@0
|
158 $data[$calc][] = $value; |
franck@0
|
159 } |
franck@0
|
160 } |
franck@0
|
161 } |
franck@0
|
162 } |
franck@0
|
163 |
franck@0
|
164 // Get chart settings from options form. |
franck@0
|
165 $legend_field = $this->view->field[$this->options['aggregation_field']]; |
franck@0
|
166 $chart = array( |
franck@0
|
167 '#type' => $this->options['format'], |
franck@0
|
168 '#height' => $this->options['height'], |
franck@0
|
169 '#width' => $this->options['width'], |
franck@0
|
170 '#color' => $this->options['color'], |
franck@0
|
171 ); |
franck@0
|
172 |
franck@0
|
173 // Use the view title as the chart title. |
franck@0
|
174 $chart['#title'] = $this->view->get_title(); |
franck@0
|
175 |
franck@0
|
176 // Insert series into the chart array. |
franck@0
|
177 foreach ($data as $series) { |
franck@0
|
178 $chart[] = $series; |
franck@0
|
179 } |
franck@0
|
180 |
franck@0
|
181 // Print the chart. |
franck@0
|
182 return charts_chart($chart); |
franck@0
|
183 } |
franck@0
|
184 |
franck@0
|
185 function query() { |
franck@0
|
186 parent::query(); |
franck@0
|
187 |
franck@0
|
188 // Clear the fields out, we'll replace them with calculated values. |
franck@0
|
189 $this->view->query->clear_fields(); |
franck@0
|
190 // Clear out any sorting, it can create unexpected results |
franck@0
|
191 // when Views adds aggregation values for the sorts. |
franck@0
|
192 $this->view->query->orderby = array(); |
franck@0
|
193 |
franck@0
|
194 // Add the grouping information to the query. |
franck@0
|
195 // Field setting of array('aggregate' => TRUE) tells Views not to force |
franck@0
|
196 // another aggregation in for this field. |
franck@0
|
197 |
franck@0
|
198 foreach ($this->view->field as $field) { |
franck@0
|
199 $query_field = substr($field->field, 0, 3) == 'cid' ? $field->definition['calc'] : $field->table .'.'. $field->field; |
franck@0
|
200 $query_alias = $field->field_alias; |
franck@0
|
201 |
franck@0
|
202 // Add the aggregation. |
franck@0
|
203 if ($field->field == $this->options['aggregation_field']) { |
franck@0
|
204 $this->view->query->add_orderby(NULL, NULL, 'asc', $query_alias); |
franck@0
|
205 $this->view->query->add_groupby($query_field); |
franck@0
|
206 if (substr($field->field, 0, 3) == 'cid') { |
franck@0
|
207 $this->view->query->add_field(NULL, $query_field, $field->field, array('aggregate' => TRUE)); |
franck@0
|
208 } |
franck@0
|
209 else { |
franck@0
|
210 $this->view->query->add_field($field->table, $field->field, NULL, array('aggregate' => TRUE)); |
franck@0
|
211 } |
franck@0
|
212 } |
franck@0
|
213 // Add computed values. |
franck@0
|
214 if (in_array($field->field, $this->calc_fields())) { |
franck@0
|
215 $sql = "ROUND(". $this->options['calc'] ."($query_field), ". $this->options['precision'] .")"; |
franck@0
|
216 $this->view->query->add_field(NULL, $sql, $field->field, array('aggregate' => TRUE)); |
franck@0
|
217 |
franck@0
|
218 // TODO This part is not relationship-safe, needs additional work |
franck@0
|
219 // to join in the right table if the computation is done |
franck@0
|
220 // on a field that comes from a relationship. |
franck@0
|
221 |
franck@0
|
222 // Make sure the table with the right alias name is available |
franck@0
|
223 // (it might have been dropped during Views optimizations.) |
franck@0
|
224 $this->view->query->add_table($field->table); |
franck@0
|
225 } |
franck@0
|
226 } |
franck@0
|
227 } |
franck@0
|
228 } |