/lib/classes/chart_base.php

https://github.com/sbourget/moodle · PHP · 306 lines · 110 code · 29 blank · 167 comment · 10 complexity · 341f26e7acea6f189f2debfd2080f193 MD5 · raw file

  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * Chart base.
  18. *
  19. * @package core
  20. * @copyright 2016 Frédéric Massart - FMCorz.net
  21. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  22. */
  23. namespace core;
  24. defined('MOODLE_INTERNAL') || die();
  25. use coding_exception;
  26. use JsonSerializable;
  27. use renderable;
  28. /**
  29. * Chart base class.
  30. *
  31. * @package core
  32. * @copyright 2016 Frédéric Massart - FMCorz.net
  33. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  34. */
  35. class chart_base implements JsonSerializable, renderable {
  36. /** @var chart_series[] The series constituting this chart. */
  37. protected $series = [];
  38. /** @var string[] The labels for the X axis when categorised. */
  39. protected $labels = [];
  40. /** @var string The title of the chart. */
  41. protected $title = null;
  42. /** @var chart_axis[] The X axes. */
  43. protected $xaxes = [];
  44. /** @var chart_axis[] The Y axes. */
  45. protected $yaxes = [];
  46. /** @var array Options for the chart legend. */
  47. protected $legendoptions = [];
  48. /**
  49. * Constructor.
  50. *
  51. * Must not take any argument.
  52. *
  53. * Most of the time you do not want to extend this, rather extend the
  54. * method {@link self::set_defaults} to set the defaults on instantiation.
  55. */
  56. public function __construct() {
  57. $this->set_defaults();
  58. }
  59. /**
  60. * Add a series to the chart.
  61. *
  62. * @param chart_series $serie The serie.
  63. */
  64. public function add_series(chart_series $serie) {
  65. $this->series[] = $serie;
  66. }
  67. /**
  68. * Serialize the object.
  69. *
  70. * @return array
  71. */
  72. public function jsonSerialize() {
  73. global $CFG;
  74. return [
  75. 'type' => $this->get_type(),
  76. 'series' => $this->series,
  77. 'labels' => $this->labels,
  78. 'title' => $this->title,
  79. 'axes' => [
  80. 'x' => $this->xaxes,
  81. 'y' => $this->yaxes,
  82. ],
  83. 'legend_options' => !empty($this->legendoptions) ? $this->legendoptions : null,
  84. 'config_colorset' => !empty($CFG->chart_colorset) ? $CFG->chart_colorset : null
  85. ];
  86. }
  87. /**
  88. * Get an axis.
  89. *
  90. * @param string $type Accepts values 'x' or 'y'.
  91. * @param int $index The index of this axis.
  92. * @param bool $createifnotexists Whether to create the axis if not found.
  93. * @return chart_axis
  94. */
  95. private function get_axis($type, $index, $createifnotexists) {
  96. $isx = $type === 'x';
  97. if ($isx) {
  98. $axis = isset($this->xaxes[$index]) ? $this->xaxes[$index] : null;
  99. } else {
  100. $axis = isset($this->yaxes[$index]) ? $this->yaxes[$index] : null;
  101. }
  102. if ($axis === null) {
  103. if (!$createifnotexists) {
  104. throw new coding_exception('Unknown axis.');
  105. }
  106. $axis = new chart_axis();
  107. if ($isx) {
  108. $this->set_xaxis($axis, $index);
  109. } else {
  110. $this->set_yaxis($axis, $index);
  111. }
  112. }
  113. return $axis;
  114. }
  115. /**
  116. * Get the labels of the X axis.
  117. *
  118. * @return string[]
  119. */
  120. public function get_labels() {
  121. return $this->labels;
  122. }
  123. /**
  124. * Get an array of options for the chart legend.
  125. *
  126. * @return array
  127. */
  128. public function get_legend_options() {
  129. return $this->legendoptions;
  130. }
  131. /**
  132. * Get the series.
  133. *
  134. * @return chart_series[]
  135. */
  136. public function get_series() {
  137. return $this->series;
  138. }
  139. /**
  140. * Get the title.
  141. *
  142. * @return string
  143. */
  144. public function get_title() {
  145. return $this->title;
  146. }
  147. /**
  148. * Get the chart type.
  149. *
  150. * @return string
  151. */
  152. public function get_type() {
  153. $classname = get_class($this);
  154. return substr($classname, strpos($classname, '_') + 1);
  155. }
  156. /**
  157. * Get the X axes.
  158. *
  159. * @return chart_axis[]
  160. */
  161. public function get_xaxes() {
  162. return $this->xaxes;
  163. }
  164. /**
  165. * Get an X axis.
  166. *
  167. * @param int $index The index of the axis.
  168. * @param bool $createifnotexists When true, create an instance of the axis if none exist at this index yet.
  169. * @return chart_axis
  170. */
  171. public function get_xaxis($index = 0, $createifnotexists = false) {
  172. return $this->get_axis('x', $index, $createifnotexists);
  173. }
  174. /**
  175. * Get the Y axes.
  176. *
  177. * @return chart_axis[]
  178. */
  179. public function get_yaxes() {
  180. return $this->yaxes;
  181. }
  182. /**
  183. * Get a Y axis.
  184. *
  185. * @param int $index The index of the axis.
  186. * @param bool $createifnotexists When true, create an instance of the axis if none exist at this index yet.
  187. * @return chart_axis
  188. */
  189. public function get_yaxis($index = 0, $createifnotexists = false) {
  190. return $this->get_axis('y', $index, $createifnotexists);
  191. }
  192. /**
  193. * Set the defaults for this chart type.
  194. *
  195. * Child classes can extend this to set default values on instantiation.
  196. *
  197. * In general the constructor could be used, but this method is here to
  198. * emphasize and self-document the default values set by the chart type.
  199. *
  200. * @return void
  201. */
  202. protected function set_defaults() {
  203. }
  204. /**
  205. * Set the chart labels.
  206. *
  207. * @param string[] $labels The labels.
  208. */
  209. public function set_labels(array $labels) {
  210. $this->labels = $labels;
  211. }
  212. /**
  213. * Set options for the chart legend.
  214. * See https://www.chartjs.org/docs/2.7.0/configuration/legend.html for options.
  215. *
  216. * Note: Setting onClick and onHover events is not directly supported through
  217. * this method. These config options must be set directly within Javascript
  218. * on the page.
  219. *
  220. * @param array $legendoptions Whether or not to display the chart's legend.
  221. */
  222. public function set_legend_options(array $legendoptions) {
  223. $this->legendoptions = $legendoptions;
  224. }
  225. /**
  226. * Set the title.
  227. *
  228. * @param string $title The title.
  229. */
  230. public function set_title($title) {
  231. $this->title = $title;
  232. }
  233. /**
  234. * Set an X axis.
  235. *
  236. * Note that this will override any predefined axis without warning.
  237. *
  238. * @param chart_axis $axis The axis.
  239. * @param int $index The index of the axis.
  240. */
  241. public function set_xaxis(chart_axis $axis, $index = 0) {
  242. $this->validate_axis('x', $axis, $index);
  243. return $this->xaxes[$index] = $axis;
  244. }
  245. /**
  246. * Set an Y axis.
  247. *
  248. * Note that this will override any predefined axis without warning.
  249. *
  250. * @param chart_axis $axis The axis.
  251. * @param int $index The index of the axis.
  252. */
  253. public function set_yaxis(chart_axis $axis, $index = 0) {
  254. $this->validate_axis('y', $axis, $index);
  255. return $this->yaxes[$index] = $axis;
  256. }
  257. /**
  258. * Validate an axis.
  259. *
  260. * We validate this from PHP because not doing it here could result in errors being
  261. * hard to trace down. For instance, if we were to add axis at keys without another
  262. * axis preceding, we would effectively contain the axes in an associative array
  263. * rather than a simple array, and that would have consequences on serialisation.
  264. *
  265. * @param string $xy Accepts x or y.
  266. * @param chart_axis $axis The axis to validate.
  267. * @param index $index The index of the axis.
  268. */
  269. protected function validate_axis($xy, chart_axis $axis, $index = 0) {
  270. if ($index > 0) {
  271. $axes = $xy == 'x' ? $this->xaxes : $this->yaxes;
  272. if (!isset($axes[$index - 1])) {
  273. throw new coding_exception('Missing ' . $xy . ' axis at index lower than ' . $index);
  274. }
  275. }
  276. }
  277. }