Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

   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       * @throws Exception
 119       *
 120       * @return DataSeriesValues
 121       */
 122      public function setDataType($dataType)
 123      {
 124          if (!in_array($dataType, self::$dataTypeValues)) {
 125              throw new Exception('Invalid datatype for chart data series values');
 126          }
 127          $this->dataType = $dataType;
 128  
 129          return $this;
 130      }
 131  
 132      /**
 133       * Get Series Data Source (formula).
 134       *
 135       * @return string
 136       */
 137      public function getDataSource()
 138      {
 139          return $this->dataSource;
 140      }
 141  
 142      /**
 143       * Set Series Data Source (formula).
 144       *
 145       * @param string $dataSource
 146       *
 147       * @return DataSeriesValues
 148       */
 149      public function setDataSource($dataSource)
 150      {
 151          $this->dataSource = $dataSource;
 152  
 153          return $this;
 154      }
 155  
 156      /**
 157       * Get Point Marker.
 158       *
 159       * @return string
 160       */
 161      public function getPointMarker()
 162      {
 163          return $this->pointMarker;
 164      }
 165  
 166      /**
 167       * Set Point Marker.
 168       *
 169       * @param string $marker
 170       *
 171       * @return DataSeriesValues
 172       */
 173      public function setPointMarker($marker)
 174      {
 175          $this->pointMarker = $marker;
 176  
 177          return $this;
 178      }
 179  
 180      /**
 181       * Get Series Format Code.
 182       *
 183       * @return string
 184       */
 185      public function getFormatCode()
 186      {
 187          return $this->formatCode;
 188      }
 189  
 190      /**
 191       * Set Series Format Code.
 192       *
 193       * @param string $formatCode
 194       *
 195       * @return DataSeriesValues
 196       */
 197      public function setFormatCode($formatCode)
 198      {
 199          $this->formatCode = $formatCode;
 200  
 201          return $this;
 202      }
 203  
 204      /**
 205       * Get Series Point Count.
 206       *
 207       * @return int
 208       */
 209      public function getPointCount()
 210      {
 211          return $this->pointCount;
 212      }
 213  
 214      /**
 215       * Get fill color.
 216       *
 217       * @return string|string[] HEX color or array with HEX colors
 218       */
 219      public function getFillColor()
 220      {
 221          return $this->fillColor;
 222      }
 223  
 224      /**
 225       * Set fill color for series.
 226       *
 227       * @param string|string[] $color HEX color or array with HEX colors
 228       *
 229       * @return   DataSeriesValues
 230       */
 231      public function setFillColor($color)
 232      {
 233          if (is_array($color)) {
 234              foreach ($color as $colorValue) {
 235                  $this->validateColor($colorValue);
 236              }
 237          } else {
 238              $this->validateColor($color);
 239          }
 240          $this->fillColor = $color;
 241  
 242          return $this;
 243      }
 244  
 245      /**
 246       * Method for validating hex color.
 247       *
 248       * @param string $color value for color
 249       *
 250       * @throws \Exception thrown if color is invalid
 251       *
 252       * @return bool true if validation was successful
 253       */
 254      private function validateColor($color)
 255      {
 256          if (!preg_match('/^[a-f0-9]{6}$/i', $color)) {
 257              throw new Exception(sprintf('Invalid hex color for chart series (color: "%s")', $color));
 258          }
 259  
 260          return true;
 261      }
 262  
 263      /**
 264       * Get line width for series.
 265       *
 266       * @return int
 267       */
 268      public function getLineWidth()
 269      {
 270          return $this->lineWidth;
 271      }
 272  
 273      /**
 274       * Set line width for the series.
 275       *
 276       * @param int $width
 277       *
 278       * @return DataSeriesValues
 279       */
 280      public function setLineWidth($width)
 281      {
 282          $minWidth = 12700;
 283          $this->lineWidth = max($minWidth, $width);
 284  
 285          return $this;
 286      }
 287  
 288      /**
 289       * Identify if the Data Series is a multi-level or a simple series.
 290       *
 291       * @return null|bool
 292       */
 293      public function isMultiLevelSeries()
 294      {
 295          if (count($this->dataValues) > 0) {
 296              return is_array(array_values($this->dataValues)[0]);
 297          }
 298  
 299          return null;
 300      }
 301  
 302      /**
 303       * Return the level count of a multi-level Data Series.
 304       *
 305       * @return int
 306       */
 307      public function multiLevelCount()
 308      {
 309          $levelCount = 0;
 310          foreach ($this->dataValues as $dataValueSet) {
 311              $levelCount = max($levelCount, count($dataValueSet));
 312          }
 313  
 314          return $levelCount;
 315      }
 316  
 317      /**
 318       * Get Series Data Values.
 319       *
 320       * @return array of mixed
 321       */
 322      public function getDataValues()
 323      {
 324          return $this->dataValues;
 325      }
 326  
 327      /**
 328       * Get the first Series Data value.
 329       *
 330       * @return mixed
 331       */
 332      public function getDataValue()
 333      {
 334          $count = count($this->dataValues);
 335          if ($count == 0) {
 336              return null;
 337          } elseif ($count == 1) {
 338              return $this->dataValues[0];
 339          }
 340  
 341          return $this->dataValues;
 342      }
 343  
 344      /**
 345       * Set Series Data Values.
 346       *
 347       * @param array $dataValues
 348       *
 349       * @return DataSeriesValues
 350       */
 351      public function setDataValues($dataValues)
 352      {
 353          $this->dataValues = Functions::flattenArray($dataValues);
 354          $this->pointCount = count($dataValues);
 355  
 356          return $this;
 357      }
 358  
 359      public function refresh(Worksheet $worksheet, $flatten = true)
 360      {
 361          if ($this->dataSource !== null) {
 362              $calcEngine = Calculation::getInstance($worksheet->getParent());
 363              $newDataValues = Calculation::unwrapResult(
 364                  $calcEngine->_calculateFormulaValue(
 365                      '=' . $this->dataSource,
 366                      null,
 367                      $worksheet->getCell('A1')
 368                  )
 369              );
 370              if ($flatten) {
 371                  $this->dataValues = Functions::flattenArray($newDataValues);
 372                  foreach ($this->dataValues as &$dataValue) {
 373                      if (is_string($dataValue) && !empty($dataValue) && $dataValue[0] == '#') {
 374                          $dataValue = 0.0;
 375                      }
 376                  }
 377                  unset($dataValue);
 378              } else {
 379                  [$worksheet, $cellRange] = Worksheet::extractSheetTitle($this->dataSource, true);
 380                  $dimensions = Coordinate::rangeDimension(str_replace('$', '', $cellRange));
 381                  if (($dimensions[0] == 1) || ($dimensions[1] == 1)) {
 382                      $this->dataValues = Functions::flattenArray($newDataValues);
 383                  } else {
 384                      $newArray = array_values(array_shift($newDataValues));
 385                      foreach ($newArray as $i => $newDataSet) {
 386                          $newArray[$i] = [$newDataSet];
 387                      }
 388  
 389                      foreach ($newDataValues as $newDataSet) {
 390                          $i = 0;
 391                          foreach ($newDataSet as $newDataVal) {
 392                              array_unshift($newArray[$i++], $newDataVal);
 393                          }
 394                      }
 395                      $this->dataValues = $newArray;
 396                  }
 397              }
 398              $this->pointCount = count($this->dataValues);
 399          }
 400      }
 401  }