These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /** |
||
4 | * A query printer for bar, line, pie and donut chart on aggregated values |
||
5 | * using the jqPlot JavaScript library. |
||
6 | * |
||
7 | * @since 1.8 |
||
8 | * |
||
9 | * @file SRF_jqPlotChart.php |
||
10 | * @ingroup SemanticResultFormats |
||
11 | * @licence GNU GPL v2 or later |
||
12 | * |
||
13 | * @author mwjames |
||
14 | * @author Jeroen De Dauw < [email protected] > |
||
15 | * @author Yaron Koren |
||
16 | * @author Sanyam Goyal |
||
17 | */ |
||
18 | class SRFjqPlotChart extends SRFjqPlot { |
||
19 | |||
20 | /** |
||
21 | * Corresponding message name |
||
22 | * |
||
23 | */ |
||
24 | public function getName() { |
||
25 | return wfMessage( 'srf-printername-jqplotchart' )->text(); |
||
26 | } |
||
27 | |||
28 | /** |
||
29 | * Prepare data output |
||
30 | * |
||
31 | * @since 1.8 |
||
32 | * |
||
33 | * @param array $data label => value |
||
34 | */ |
||
35 | protected function getFormatOutput( array $data ) { |
||
36 | |||
37 | static $statNr = 0; |
||
38 | $chartID = 'jqplot-' . $this->params['charttype'] . '-' . ++$statNr; |
||
39 | |||
40 | $this->isHTML = true; |
||
41 | |||
42 | // Prepare data objects |
||
43 | if ( in_array( $this->params['charttype'], [ 'bar', 'line' ] ) ) { |
||
44 | // Parse bar relevant data |
||
45 | $dataObject = $this->prepareBarData( $data ); |
||
46 | } elseif ( in_array( $this->params['charttype'], [ 'pie', 'donut' ] ) ) { |
||
47 | //Parse pie/donut relevant data |
||
48 | $dataObject = $this->preparePieData( $data ); |
||
49 | } else { |
||
50 | // Return with an error |
||
51 | return Html::rawElement( |
||
52 | 'span', |
||
53 | [ |
||
54 | 'class' => "error" |
||
55 | ], |
||
56 | wfMessage( 'srf-error-missing-layout' )->inContentLanguage()->text() |
||
57 | ); |
||
58 | } |
||
59 | |||
60 | // Encode data objects |
||
61 | $requireHeadItem = [ $chartID => FormatJson::encode( $dataObject ) ]; |
||
62 | SMWOutputs::requireHeadItem( $chartID, Skin::makeVariablesScript( $requireHeadItem ) ); |
||
63 | |||
64 | // Processing placeholder |
||
65 | $processing = SRFUtils::htmlProcessingElement( $this->isHTML ); |
||
66 | |||
67 | // Ensure right conversion |
||
68 | $width = strstr( $this->params['width'], "%" ) ? $this->params['width'] : $this->params['width'] . 'px'; |
||
69 | |||
70 | // Chart/graph placeholder |
||
71 | $chart = Html::rawElement( |
||
72 | 'div', |
||
73 | [ |
||
74 | 'id' => $chartID, |
||
75 | 'class' => 'container', |
||
76 | 'style' => "display:none; width: {$width}; height: {$this->params['height']}px;" |
||
77 | ], |
||
78 | null |
||
79 | ); |
||
80 | |||
81 | // Beautify class selector |
||
82 | $class = $this->params['charttype'] ? '-' . $this->params['charttype'] : ''; |
||
83 | $class = $this->params['class'] ? $class . ' ' . $this->params['class'] : $class . ' jqplot-common'; |
||
84 | |||
85 | // Chart/graph wrappper |
||
86 | return Html::rawElement( |
||
87 | 'div', |
||
88 | [ |
||
89 | 'class' => 'srf-jqplot' . $class, |
||
90 | ], |
||
91 | $processing . $chart |
||
92 | ); |
||
93 | } |
||
94 | |||
95 | /** |
||
96 | * Prepare pie/donut chart specific data and parameters |
||
97 | * |
||
98 | * @since 1.8 |
||
99 | * |
||
100 | * @param array $rawdata label => value |
||
101 | * |
||
102 | * @return array |
||
103 | */ |
||
104 | private function preparePieData( $rawdata ) { |
||
105 | |||
106 | // Init |
||
107 | $mode = 'single'; |
||
108 | |||
109 | // Reorganize the data in accordance with the pie chart req. |
||
110 | foreach ( $rawdata as $name => $value ) { |
||
111 | if ( $value >= $this->params['min'] ) { |
||
112 | $data[] = [ $name, $value ]; |
||
0 ignored issues
–
show
|
|||
113 | } |
||
114 | } |
||
115 | |||
116 | if ( $this->params['charttype'] === 'donut' ) { |
||
117 | SMWOutputs::requireResource( 'ext.srf.jqplot.donut' ); |
||
118 | } else { |
||
119 | SMWOutputs::requireResource( 'ext.srf.jqplot.pie' ); |
||
120 | } |
||
121 | |||
122 | return [ |
||
123 | 'data' => [ $data ], |
||
124 | 'renderer' => $this->params['charttype'], |
||
125 | 'mode' => $mode, |
||
126 | 'parameters' => $this->addCommonOptions() |
||
127 | ]; |
||
128 | } |
||
129 | |||
130 | /** |
||
131 | * Prepare bar/line chart specific data and parameters |
||
132 | * |
||
133 | * Data can be an array of y values, or an array of [label, value] pairs; |
||
134 | * While labels are used only on the first series with labels on |
||
135 | * subsequent series being ignored |
||
136 | * |
||
137 | * @since 1.8 |
||
138 | * |
||
139 | * @param array $rawdata label => value |
||
140 | * |
||
141 | * @return array |
||
142 | */ |
||
143 | private function prepareBarData( $rawdata ) { |
||
144 | |||
145 | // Init |
||
146 | $total = 0; |
||
147 | $mode = 'single'; |
||
148 | |||
149 | // Find min and max values to determine the graphs axis parameter |
||
150 | $maxValue = count( $rawdata ) == 0 ? 0 : max( $rawdata ); |
||
151 | |||
152 | if ( $this->params['min'] === false ) { |
||
153 | $minValue = count( $rawdata ) == 0 ? 0 : min( $rawdata ); |
||
154 | } else { |
||
155 | $minValue = $this->params['min']; |
||
156 | } |
||
157 | |||
158 | // Get number ticks |
||
159 | $data['numbersticks'] = SRFjqPlot::getNumbersTicks( $minValue, $maxValue ); |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code. Let’s take a look at an example: foreach ($collection as $item) {
$myArray['foo'] = $item->getFoo();
if ($item->hasBar()) {
$myArray['bar'] = $item->getBar();
}
// do something with $myArray
}
As you can see in this example, the array This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.
Loading history...
|
|||
160 | |||
161 | // Reorganize the data in accordance with the bar/line chart req. |
||
162 | foreach ( $rawdata as $key => $value ) { |
||
163 | if ( $value >= $this->params['min'] ) { |
||
164 | $data['series'][] = [ $key, $value ]; |
||
165 | $total = $total + $value; |
||
166 | } |
||
167 | } |
||
168 | |||
169 | // Bar/line module |
||
170 | SMWOutputs::requireResource( 'ext.srf.jqplot.bar' ); |
||
171 | |||
172 | // Highlighter plugin |
||
173 | if ( $this->params['highlighter'] ) { |
||
174 | SMWOutputs::requireResource( 'ext.srf.jqplot.highlighter' ); |
||
175 | } |
||
176 | |||
177 | // Pointlabels plugin |
||
178 | if ( in_array( $this->params['datalabels'], [ 'value', 'label', 'percent' ] ) ) { |
||
179 | SMWOutputs::requireResource( 'ext.srf.jqplot.pointlabels' ); |
||
180 | } |
||
181 | |||
182 | return [ |
||
183 | 'data' => [ $data['series'] ], |
||
184 | 'ticks' => $data['numbersticks'], |
||
185 | 'labels' => array_keys( $data['series'] ), |
||
186 | 'numbers' => array_values( $data['series'] ), |
||
187 | 'max' => $maxValue, |
||
188 | 'total' => $total, |
||
189 | 'mode' => $mode, |
||
190 | 'series' => [], |
||
191 | 'renderer' => $this->params['charttype'], |
||
192 | 'parameters' => $this->addCommonOptions() |
||
193 | ]; |
||
194 | } |
||
195 | |||
196 | /** |
||
197 | * jqPlot common parameters |
||
198 | * |
||
199 | * @since 1.8 |
||
200 | * |
||
201 | */ |
||
202 | private function addCommonOptions() { |
||
203 | |||
204 | // Series colour |
||
205 | $seriescolors = $this->params['chartcolor'] !== '' ? array_filter( |
||
206 | explode( ",", $this->params['chartcolor'] ) |
||
207 | ) : null; |
||
208 | |||
209 | return [ |
||
210 | 'numbersaxislabel' => $this->params['numbersaxislabel'], |
||
211 | 'labelaxislabel' => $this->params['labelaxislabel'], |
||
212 | 'charttitle' => $this->params['charttitle'], |
||
213 | 'charttext' => $this->params['charttext'], |
||
214 | 'theme' => $this->params['theme'] ? $this->params['theme'] : null, |
||
215 | 'ticklabels' => $this->params['ticklabels'], |
||
216 | 'highlighter' => $this->params['highlighter'], |
||
217 | 'direction' => $this->params['direction'], |
||
218 | 'smoothlines' => $this->params['smoothlines'], |
||
219 | 'filling' => $this->params['filling'], |
||
220 | 'datalabels' => $this->params['datalabels'], |
||
221 | 'valueformat' => $this->params['valueformat'], |
||
222 | 'chartlegend' => $this->params['chartlegend'] !== '' ? $this->params['chartlegend'] : 'none', |
||
223 | 'colorscheme' => $this->params['colorscheme'] !== '' ? $this->params['colorscheme'] : null, |
||
224 | 'pointlabels' => $this->params['datalabels'] === 'none' ? false : $this->params['datalabels'], |
||
225 | 'grid' => $this->params['theme'] === 'vector' ? [ 'borderColor' => '#a7d7f9' ] : ( $this->params['theme'] === 'simple' ? [ 'borderColor' => '#ddd' ] : null ), |
||
226 | 'seriescolors' => $seriescolors |
||
227 | ]; |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * @see SMWResultPrinter::getParamDefinitions |
||
232 | * |
||
233 | * @since 1.8 |
||
234 | * |
||
235 | * @param $definitions array of IParamDefinition |
||
236 | * |
||
237 | * @return array of IParamDefinition|array |
||
238 | */ |
||
239 | public function getParamDefinitions( array $definitions ) { |
||
240 | $params = self::getCommonParams(); |
||
241 | |||
242 | $params['charttype'] = [ |
||
243 | 'message' => 'srf-paramdesc-charttype', |
||
244 | 'default' => 'bar', |
||
245 | 'values' => [ 'bar', 'line', 'pie', 'donut' ], |
||
246 | ]; |
||
247 | |||
248 | return array_merge( parent::getParamDefinitions( $definitions ), $params ); |
||
249 | } |
||
250 | } |
||
251 |
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.
Let’s take a look at an example:
As you can see in this example, the array
$myArray
is initialized the first time when the foreach loop is entered. You can also see that the value of thebar
key is only written conditionally; thus, its value might result from a previous iteration.This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.