Merge pull request #24022 from colemanw/afformFrontend
[civicrm-core.git] / CRM / Utils / Chart.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17
18 /**
19 * Build various graphs using the dc chart library.
20 */
21 class CRM_Utils_Chart {
22
23 /**
24 * Build The Bar Graph.
25 *
26 * @param array $params
27 * Assoc array of name/value pairs.
28 *
29 * @return object
30 * $chart object data used for client-side chart rendering (currently with dc chart library).
31 */
32 public static function barChart($params) {
33 $output = static::commonParamsManipulation($params);
34 if (empty($output)) {
35 return NULL;
36 }
37 $output['type'] = 'barchart';
38 // Default title
39 $output += ['title' => ts('Bar Chart')];
40
41 // ? Not sure what reports use this, but it's not implemented.
42 // call user define function to handle on click event.
43 // if ($onClickFunName = CRM_Utils_Array::value('on_click_fun_name', $params)) {
44 // $bars[$barCount]->set_on_click($onClickFunName);
45 // }
46
47 //// get the currency to set in tooltip.
48 //$tooltip = CRM_Utils_Array::value('tip', $params, "$symbol #val#");
49
50 return $output;
51 }
52
53 /**
54 * Build a pie chart.
55 *
56 * @param array $params
57 * Assoc array of name/value pairs.
58 *
59 * @return array
60 */
61 public static function pieChart($params) {
62 $output = static::commonParamsManipulation($params);
63 if (empty($output)) {
64 return NULL;
65 }
66 $output['type'] = 'piechart';
67 $output += ['title' => ts('Pie Chart')];
68
69 // ? Not sure what reports use this, but it's not implemented.
70 // call user define function to handle on click event.
71 // if ($onClickFunName = CRM_Utils_Array::value('on_click_fun_name', $params)) {
72 // $bars[$barCount]->set_on_click($onClickFunName);
73 // }
74
75 //// get the currency to set in tooltip.
76 //$tooltip = CRM_Utils_Array::value('tip', $params, "$symbol #val#");
77
78 return $output;
79 }
80
81 /**
82 * Common data manipulation for charts.
83 *
84 * @param array $params
85 * Assoc array of name/value pairs.
86 *
87 * @return array
88 */
89 public static function commonParamsManipulation($params) {
90 if (empty($params)) {
91 return NULL;
92 }
93 $output = [];
94 if (empty($params['multiValues'])) {
95 $params['multiValues'] = [$params['values']];
96 }
97
98 $output['values'] = [];
99 foreach ($params['multiValues'] as $i => $dataSet) {
100 $output['values'][$i] = [];
101 foreach ($dataSet as $k => $v) {
102 $output['values'][$i][] = ['label' => $k, 'value' => (double) $v];
103 }
104 }
105 if (!$output['values']) {
106 return NULL;
107 }
108
109 // Ensure there's a legend (title)
110 if (!empty($params['legend'])) {
111 $output['title'] = $params['legend'];
112 }
113
114 $output['symbol'] = CRM_Core_BAO_Country::defaultCurrencySymbol();
115
116 // ? Not sure what reports use this, but it's not implemented.
117 // call user define function to handle on click event.
118 // if ($onClickFunName = CRM_Utils_Array::value('on_click_fun_name', $params)) {
119 // $bars[$barCount]->set_on_click($onClickFunName);
120 // }
121
122 //// get the currency to set in tooltip.
123 //$tooltip = CRM_Utils_Array::value('tip', $params, "$symbol #val#");
124
125 return $output;
126 }
127
128 /**
129 * @param array $rows
130 * @param string $chart
131 * @param string $interval
132 *
133 * @return array
134 */
135 public static function chart($rows, $chart, $interval) {
136 $lcInterval = strtolower($interval);
137 $label = ucfirst($lcInterval);
138 $chartData = $dateKeys = [];
139 $intervalLabels = [
140 'year' => ts('Yearly'),
141 'fiscalyear' => ts('Yearly (Fiscal)'),
142 'month' => ts('Monthly'),
143 'quarter' => ts('Quarterly'),
144 'week' => ts('Weekly'),
145 'yearweek' => ts('Weekly'),
146 ];
147
148 switch ($lcInterval) {
149 case 'month':
150 case 'quarter':
151 case 'week':
152 case 'yearweek':
153 foreach ($rows['receive_date'] as $key => $val) {
154 list($year, $month) = explode('-', $val);
155 $dateKeys[] = substr($rows[$interval][$key], 0, 3) . ' of ' . $year;
156 }
157 $legend = $intervalLabels[$lcInterval];
158 break;
159
160 default:
161 foreach ($rows['receive_date'] as $key => $val) {
162 list($year, $month) = explode('-', $val);
163 $dateKeys[] = $year;
164 }
165 $legend = ts("%1", [1 => $label]);
166 if (!empty($intervalLabels[$lcInterval])) {
167 $legend = $intervalLabels[$lcInterval];
168 }
169 break;
170 }
171
172 if (!empty($dateKeys)) {
173 $graph = [];
174 if (!array_key_exists('multiValue', $rows)) {
175 $rows['multiValue'] = [$rows['value']];
176 }
177 foreach ($rows['multiValue'] as $key => $val) {
178 $graph[$key] = array_combine($dateKeys, $rows['multiValue'][$key]);
179 }
180 $chartData = [
181 'legend' => "$legend " . CRM_Utils_Array::value('legend', $rows, ts('Contribution')) . ' ' . ts('Summary'),
182 'values' => $graph[0],
183 'multiValues' => $graph,
184 'barKeys' => $rows['barKeys'] ?? [],
185 ];
186 }
187
188 // rotate the x labels.
189 $chartData['xLabelAngle'] = $rows['xLabelAngle'] ?? 0;
190 if (!empty($rows['tip'])) {
191 $chartData['tip'] = $rows['tip'];
192 }
193
194 // legend
195 $chartData['xname'] = $rows['xname'] ?? NULL;
196 $chartData['yname'] = $rows['yname'] ?? NULL;
197
198 // carry some chart params if pass.
199 foreach ([
200 'xSize',
201 'ySize',
202 'divName',
203 ] as $f) {
204 if (!empty($rows[$f])) {
205 $chartData[$f] = $rows[$f];
206 }
207 }
208
209 return self::buildChart($chartData, $chart);
210 }
211
212 /**
213 * @param array $rows
214 * @param string $chart
215 * @param array $interval
216 * @param array $chartInfo
217 *
218 * @return array
219 */
220 public static function reportChart($rows, $chart, $interval, &$chartInfo) {
221 foreach ($interval as $key => $val) {
222 $graph[$val] = $rows['value'][$key];
223 }
224
225 $chartData = [
226 'values' => $graph,
227 'legend' => $chartInfo['legend'],
228 'xname' => $chartInfo['xname'],
229 'yname' => $chartInfo['yname'],
230 ];
231
232 // rotate the x labels.
233 $chartData['xLabelAngle'] = CRM_Utils_Array::value('xLabelAngle', $chartInfo, 20);
234 if (!empty($chartInfo['tip'])) {
235 $chartData['tip'] = $chartInfo['tip'];
236 }
237
238 // carry some chart params if pass.
239 foreach ([
240 'xSize',
241 'ySize',
242 'divName',
243 ] as $f) {
244 if (!empty($rows[$f])) {
245 $chartData[$f] = $rows[$f];
246 }
247 }
248
249 return self::buildChart($chartData, $chart);
250 }
251
252 /**
253 * @param array $params
254 * @param string $chart
255 *
256 * @return array
257 */
258 public static function buildChart(&$params, $chart) {
259 $theChart = [];
260 if ($chart && is_array($params) && !empty($params)) {
261 // build the chart objects.
262 $chartObj = CRM_Utils_Chart::$chart($params);
263
264 if ($chartObj) {
265 // calculate chart size.
266 $xSize = CRM_Utils_Array::value('xSize', $params, 400);
267 $ySize = CRM_Utils_Array::value('ySize', $params, 300);
268 if ($chart == 'barChart') {
269 $ySize = CRM_Utils_Array::value('ySize', $params, 250);
270 $xSize = 60 * count($params['values']);
271 // hack to show tooltip.
272 if ($xSize < 200) {
273 $xSize = (count($params['values']) > 1) ? 100 * count($params['values']) : 170;
274 }
275 elseif ($xSize > 600 && count($params['values']) > 1) {
276 $xSize = (count($params['values']) + 400 / count($params['values'])) * count($params['values']);
277 }
278 }
279
280 // generate unique id for this chart instance
281 $uniqueId = md5(uniqid(rand(), TRUE));
282
283 $theChart["chart_{$uniqueId}"]['size'] = ['xSize' => $xSize, 'ySize' => $ySize];
284 $theChart["chart_{$uniqueId}"]['object'] = $chartObj;
285
286 // assign chart data to template
287 $template = CRM_Core_Smarty::singleton();
288 $template->assign('uniqueId', $uniqueId);
289 $template->assign("chartData", json_encode($theChart ?? []));
290 }
291 }
292
293 return $theChart;
294 }
295
296 }