Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 310 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]

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