Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Chart;
   4  
   5  use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
   6  use PhpOffice\PhpSpreadsheet\Calculation\Functions;
   7  use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
   8  use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
   9  
  10  class DataSeriesValues
  11  {
  12      const DATASERIES_TYPE_STRING = 'String';
  13      const DATASERIES_TYPE_NUMBER = 'Number';
  14  
  15      private static $dataTypeValues = [
  16          self::DATASERIES_TYPE_STRING,
  17          self::DATASERIES_TYPE_NUMBER,
  18      ];
  19  
  20      /**
  21       * Series Data Type.
  22       *
  23       * @var string
  24       */
  25      private $dataType;
  26  
  27      /**
  28       * Series Data Source.
  29       *
  30       * @var string
  31       */
  32      private $dataSource;
  33  
  34      /**
  35       * Format Code.
  36       *
  37       * @var string
  38       */
  39      private $formatCode;
  40  
  41      /**
  42       * Series Point Marker.
  43       *
  44       * @var string
  45       */
  46      private $pointMarker;
  47  
  48      /**
  49       * Point Count (The number of datapoints in the dataseries).
  50       *
  51       * @var int
  52       */
  53      private $pointCount = 0;
  54  
  55      /**
  56       * Data Values.
  57       *
  58       * @var array of mixed
  59       */
  60      private $dataValues = [];
  61  
  62      /**
  63       * Fill color (can be array with colors if dataseries have custom colors).
  64       *
  65       * @var string|string[]
  66       */
  67      private $fillColor;
  68  
  69      /**
  70       * Line Width.
  71       *
  72       * @var int
  73       */
  74      private $lineWidth = 12700;
  75  
  76      /**
  77       * Create a new DataSeriesValues object.
  78       *
  79       * @param string $dataType
  80       * @param string $dataSource
  81       * @param null|mixed $formatCode
  82       * @param int $pointCount
  83       * @param mixed $dataValues
  84       * @param null|mixed $marker
  85       * @param null|string|string[] $fillColor
  86       */
  87      public function __construct($dataType = self::DATASERIES_TYPE_NUMBER, $dataSource = null, $formatCode = null, $pointCount = 0, $dataValues = [], $marker = null, $fillColor = null)
  88      {
  89          $this->setDataType($dataType);
  90          $this->dataSource = $dataSource;
  91          $this->formatCode = $formatCode;
  92          $this->pointCount = $pointCount;
  93          $this->dataValues = $dataValues;
  94          $this->pointMarker = $marker;
  95          $this->fillColor = $fillColor;
  96      }
  97  
  98      /**
  99       * Get Series Data Type.
 100       *
 101       * @return string
 102       */
 103      public function getDataType()
 104      {
 105          return $this->dataType;
 106      }
 107  
 108      /**
 109       * Set Series Data Type.
 110       *
 111       * @param string $dataType Datatype of this data series
 112       *                                Typical values are:
 113       *                                    DataSeriesValues::DATASERIES_TYPE_STRING
 114       *                                        Normally used for axis point values
 115       *                                    DataSeriesValues::DATASERIES_TYPE_NUMBER
 116       *                                        Normally used for chart data values
 117       *
 118       * @return $this
 119       */
 120      public function setDataType($dataType)
 121      {
 122          if (!in_array($dataType, self::$dataTypeValues)) {
 123              throw new Exception('Invalid datatype for chart data series values');
 124          }
 125          $this->dataType = $dataType;
 126  
 127          return $this;
 128      }
 129  
 130      /**
 131       * Get Series Data Source (formula).
 132       *
 133       * @return string
 134       */
 135      public function getDataSource()
 136      {
 137          return $this->dataSource;
 138      }
 139  
 140      /**
 141       * Set Series Data Source (formula).
 142       *
 143       * @param string $dataSource
 144       *
 145       * @return $this
 146       */
 147      public function setDataSource($dataSource)
 148      {
 149          $this->dataSource = $dataSource;
 150  
 151          return $this;
 152      }
 153  
 154      /**
 155       * Get Point Marker.
 156       *
 157       * @return string
 158       */
 159      public function getPointMarker()
 160      {
 161          return $this->pointMarker;
 162      }
 163  
 164      /**
 165       * Set Point Marker.
 166       *
 167       * @param string $marker
 168       *
 169       * @return $this
 170       */
 171      public function setPointMarker($marker)
 172      {
 173          $this->pointMarker = $marker;
 174  
 175          return $this;
 176      }
 177  
 178      /**
 179       * Get Series Format Code.
 180       *
 181       * @return string
 182       */
 183      public function getFormatCode()
 184      {
 185          return $this->formatCode;
 186      }
 187  
 188      /**
 189       * Set Series Format Code.
 190       *
 191       * @param string $formatCode
 192       *
 193       * @return $this
 194       */
 195      public function setFormatCode($formatCode)
 196      {
 197          $this->formatCode = $formatCode;
 198  
 199          return $this;
 200      }
 201  
 202      /**
 203       * Get Series Point Count.
 204       *
 205       * @return int
 206       */
 207      public function getPointCount()
 208      {
 209          return $this->pointCount;
 210      }
 211  
 212      /**
 213       * Get fill color.
 214       *
 215       * @return string|string[] HEX color or array with HEX colors
 216       */
 217      public function getFillColor()
 218      {
 219          return $this->fillColor;
 220      }
 221  
 222      /**
 223       * Set fill color for series.
 224       *
 225       * @param string|string[] $color HEX color or array with HEX colors
 226       *
 227       * @return   DataSeriesValues
 228       */
 229      public function setFillColor($color)
 230      {
 231          if (is_array($color)) {
 232              foreach ($color as $colorValue) {
 233                  $this->validateColor($colorValue);
 234              }
 235          } else {
 236              $this->validateColor($color);
 237          }
 238          $this->fillColor = $color;
 239  
 240          return $this;
 241      }
 242  
 243      /**
 244       * Method for validating hex color.
 245       *
 246       * @param string $color value for color
 247       *
 248       * @return bool true if validation was successful
 249       */
 250      private function validateColor($color)
 251      {
 252          if (!preg_match('/^[a-f0-9]{6}$/i', $color)) {
 253              throw new Exception(sprintf('Invalid hex color for chart series (color: "%s")', $color));
 254          }
 255  
 256          return true;
 257      }
 258  
 259      /**
 260       * Get line width for series.
 261       *
 262       * @return int
 263       */
 264      public function getLineWidth()
 265      {
 266          return $this->lineWidth;
 267      }
 268  
 269      /**
 270       * Set line width for the series.
 271       *
 272       * @param int $width
 273       *
 274       * @return $this
 275       */
 276      public function setLineWidth($width)
 277      {
 278          $minWidth = 12700;
 279          $this->lineWidth = max($minWidth, $width);
 280  
 281          return $this;
 282      }
 283  
 284      /**
 285       * Identify if the Data Series is a multi-level or a simple series.
 286       *
 287       * @return null|bool
 288       */
 289      public function isMultiLevelSeries()
 290      {
 291          if (count($this->dataValues) > 0) {
 292              return is_array(array_values($this->dataValues)[0]);
 293          }
 294  
 295          return null;
 296      }
 297  
 298      /**
 299       * Return the level count of a multi-level Data Series.
 300       *
 301       * @return int
 302       */
 303      public function multiLevelCount()
 304      {
 305          $levelCount = 0;
 306          foreach ($this->dataValues as $dataValueSet) {
 307              $levelCount = max($levelCount, count($dataValueSet));
 308          }
 309  
 310          return $levelCount;
 311      }
 312  
 313      /**
 314       * Get Series Data Values.
 315       *
 316       * @return array of mixed
 317       */
 318      public function getDataValues()
 319      {
 320          return $this->dataValues;
 321      }
 322  
 323      /**
 324       * Get the first Series Data value.
 325       *
 326       * @return mixed
 327       */
 328      public function getDataValue()
 329      {
 330          $count = count($this->dataValues);
 331          if ($count == 0) {
 332              return null;
 333          } elseif ($count == 1) {
 334              return $this->dataValues[0];
 335          }
 336  
 337          return $this->dataValues;
 338      }
 339  
 340      /**
 341       * Set Series Data Values.
 342       *
 343       * @param array $dataValues
 344       *
 345       * @return $this
 346       */
 347      public function setDataValues($dataValues)
 348      {
 349          $this->dataValues = Functions::flattenArray($dataValues);
 350          $this->pointCount = count($dataValues);
 351  
 352          return $this;
 353      }
 354  
 355      public function refresh(Worksheet $worksheet, $flatten = true): void
 356      {
 357          if ($this->dataSource !== null) {
 358              $calcEngine = Calculation::getInstance($worksheet->getParent());
 359              $newDataValues = Calculation::unwrapResult(
 360                  $calcEngine->_calculateFormulaValue(
 361                      '=' . $this->dataSource,
 362                      null,
 363                      $worksheet->getCell('A1')
 364                  )
 365              );
 366              if ($flatten) {
 367                  $this->dataValues = Functions::flattenArray($newDataValues);
 368                  foreach ($this->dataValues as &$dataValue) {
 369                      if (is_string($dataValue) && !empty($dataValue) && $dataValue[0] == '#') {
 370                          $dataValue = 0.0;
 371                      }
 372                  }
 373                  unset($dataValue);
 374              } else {
 375                  [$worksheet, $cellRange] = Worksheet::extractSheetTitle($this->dataSource, true);
 376                  $dimensions = Coordinate::rangeDimension(str_replace('$', '', $cellRange));
 377                  if (($dimensions[0] == 1) || ($dimensions[1] == 1)) {
 378                      $this->dataValues = Functions::flattenArray($newDataValues);
 379                  } else {
 380                      $newArray = array_values(array_shift($newDataValues));
 381                      foreach ($newArray as $i => $newDataSet) {
 382                          $newArray[$i] = [$newDataSet];
 383                      }
 384  
 385                      foreach ($newDataValues as $newDataSet) {
 386                          $i = 0;
 387                          foreach ($newDataSet as $newDataVal) {
 388                              array_unshift($newArray[$i++], $newDataVal);
 389                          }
 390                      }
 391                      $this->dataValues = $newArray;
 392                  }
 393              }
 394              $this->pointCount = count($this->dataValues);
 395          }
 396      }
 397  }