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 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  declare(strict_types=1);
  18  
  19  namespace core_reportbuilder\local\helpers;
  20  
  21  use stdClass;
  22  use invalid_parameter_exception;
  23  use core\persistent;
  24  use core_reportbuilder\datasource;
  25  use core_reportbuilder\manager;
  26  use core_reportbuilder\local\models\column;
  27  use core_reportbuilder\local\models\filter;
  28  use core_reportbuilder\local\models\report as report_model;
  29  
  30  /**
  31   * Helper class for manipulating custom reports and their elements (columns, filters, conditions, etc)
  32   *
  33   * @package     core_reportbuilder
  34   * @copyright   2021 Paul Holden <paulh@moodle.com>
  35   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   */
  37  class report {
  38  
  39      /**
  40       * Create custom report
  41       *
  42       * @param stdClass $data
  43       * @param bool $default If $default is set to true it will populate report with default layout as defined by the selected
  44       *                      source. These include pre-defined columns, filters and conditions.
  45       * @return report_model
  46       */
  47      public static function create_report(stdClass $data, bool $default = true): report_model {
  48          $data->name = trim($data->name);
  49          $data->type = datasource::TYPE_CUSTOM_REPORT;
  50  
  51          $reportpersistent = manager::create_report_persistent($data);
  52  
  53          // Add datasource default columns, filters and conditions to the report.
  54          if ($default) {
  55              $source = $reportpersistent->get('source');
  56              /** @var datasource $datasource */
  57              $datasource = new $source($reportpersistent, []);
  58              $datasource->add_default_columns();
  59              $datasource->add_default_filters();
  60              $datasource->add_default_conditions();
  61          }
  62  
  63          return $reportpersistent;
  64      }
  65  
  66      /**
  67       * Update custom report
  68       *
  69       * @param stdClass $data
  70       * @return report_model
  71       */
  72      public static function update_report(stdClass $data): report_model {
  73          $report = report_model::get_record(['id' => $data->id, 'type' => datasource::TYPE_CUSTOM_REPORT]);
  74          if ($report === false) {
  75              throw new invalid_parameter_exception('Invalid report');
  76          }
  77  
  78          $report->set_many([
  79              'name' => trim($data->name),
  80              'uniquerows' => $data->uniquerows,
  81          ])->update();
  82  
  83          return $report;
  84      }
  85  
  86      /**
  87       * Delete custom report
  88       *
  89       * @param int $reportid
  90       * @return bool
  91       * @throws invalid_parameter_exception
  92       */
  93      public static function delete_report(int $reportid): bool {
  94          $report = report_model::get_record(['id' => $reportid, 'type' => datasource::TYPE_CUSTOM_REPORT]);
  95          if ($report === false) {
  96              throw new invalid_parameter_exception('Invalid report');
  97          }
  98  
  99          return $report->delete();
 100      }
 101  
 102      /**
 103       * Add given column to report
 104       *
 105       * @param int $reportid
 106       * @param string $uniqueidentifier
 107       * @return column
 108       * @throws invalid_parameter_exception
 109       */
 110      public static function add_report_column(int $reportid, string $uniqueidentifier): column {
 111          $report = manager::get_report_from_id($reportid);
 112  
 113          if (!array_key_exists($uniqueidentifier, $report->get_columns())) {
 114              throw new invalid_parameter_exception('Invalid column');
 115          }
 116  
 117          $column = new column(0, (object) [
 118              'reportid' => $reportid,
 119              'uniqueidentifier' => $uniqueidentifier,
 120              'columnorder' => column::get_max_columnorder($reportid, 'columnorder') + 1,
 121              'sortorder' => column::get_max_columnorder($reportid, 'sortorder') + 1,
 122          ]);
 123  
 124          return $column->create();
 125      }
 126  
 127      /**
 128       * Delete given column from report
 129       *
 130       * @param int $reportid
 131       * @param int $columnid
 132       * @return bool
 133       * @throws invalid_parameter_exception
 134       */
 135      public static function delete_report_column(int $reportid, int $columnid): bool {
 136          global $DB;
 137  
 138          $column = column::get_record(['id' => $columnid, 'reportid' => $reportid]);
 139          if ($column === false) {
 140              throw new invalid_parameter_exception('Invalid column');
 141          }
 142  
 143          // After deletion, re-index remaining report columns.
 144          if ($result = $column->delete()) {
 145              $sqlupdateorder = '
 146                  UPDATE {' . column::TABLE . '}
 147                     SET columnorder = columnorder - 1
 148                   WHERE reportid = :reportid
 149                     AND columnorder > :columnorder';
 150  
 151              $DB->execute($sqlupdateorder, ['reportid' => $reportid, 'columnorder' => $column->get('columnorder')]);
 152          }
 153  
 154          return $result;
 155      }
 156  
 157      /**
 158       * Re-order given column within a report
 159       *
 160       * @param int $reportid
 161       * @param int $columnid
 162       * @param int $position
 163       * @return bool
 164       * @throws invalid_parameter_exception
 165       */
 166      public static function reorder_report_column(int $reportid, int $columnid, int $position): bool {
 167          $column = column::get_record(['id' => $columnid, 'reportid' => $reportid]);
 168          if ($column === false) {
 169              throw new invalid_parameter_exception('Invalid column');
 170          }
 171  
 172          // Get the rest of the report columns, excluding the one we are moving.
 173          $columns = column::get_records_select('reportid = :reportid AND id <> :id', [
 174              'reportid' => $reportid,
 175              'id' => $columnid,
 176          ], 'columnorder');
 177  
 178          return static::reorder_persistents_by_field($column, $columns, $position, 'columnorder');
 179      }
 180  
 181      /**
 182       * Re-order given column sorting within a report
 183       *
 184       * @param int $reportid
 185       * @param int $columnid
 186       * @param int $position
 187       * @return bool
 188       * @throws invalid_parameter_exception
 189       */
 190      public static function reorder_report_column_sorting(int $reportid, int $columnid, int $position): bool {
 191          $column = column::get_record(['id' => $columnid, 'reportid' => $reportid]);
 192          if ($column === false) {
 193              throw new invalid_parameter_exception('Invalid column');
 194          }
 195  
 196          // Get the rest of the report columns, excluding the one we are moving.
 197          $columns = column::get_records_select('reportid = :reportid AND id <> :id', [
 198              'reportid' => $reportid,
 199              'id' => $columnid,
 200          ], 'sortorder');
 201  
 202          return static::reorder_persistents_by_field($column, $columns, $position, 'sortorder');
 203      }
 204  
 205      /**
 206       * Toggle sorting options for given column within a report
 207       *
 208       * @param int $reportid
 209       * @param int $columnid
 210       * @param bool $enabled
 211       * @param int $direction
 212       * @return bool
 213       * @throws invalid_parameter_exception
 214       */
 215      public static function toggle_report_column_sorting(int $reportid, int $columnid, bool $enabled,
 216              int $direction = SORT_ASC): bool {
 217  
 218          $column = column::get_record(['id' => $columnid, 'reportid' => $reportid]);
 219          if ($column === false) {
 220              throw new invalid_parameter_exception('Invalid column');
 221          }
 222  
 223          return $column->set_many([
 224              'sortenabled' => $enabled,
 225              'sortdirection' => $direction,
 226          ])->update();
 227      }
 228  
 229      /**
 230       * Add given condition to report
 231       *
 232       * @param int $reportid
 233       * @param string $uniqueidentifier
 234       * @return filter
 235       * @throws invalid_parameter_exception
 236       */
 237      public static function add_report_condition(int $reportid, string $uniqueidentifier): filter {
 238          $report = manager::get_report_from_id($reportid);
 239  
 240          if (!array_key_exists($uniqueidentifier, $report->get_conditions())) {
 241              throw new invalid_parameter_exception('Invalid condition');
 242          }
 243  
 244          $condition = new filter(0, (object) [
 245              'reportid' => $reportid,
 246              'uniqueidentifier' => $uniqueidentifier,
 247              'iscondition' => true,
 248              'filterorder' => filter::get_max_filterorder($reportid, true) + 1,
 249          ]);
 250  
 251          return $condition->create();
 252      }
 253  
 254      /**
 255       * Delete given condition from report
 256       *
 257       * @param int $reportid
 258       * @param int $conditionid
 259       * @return bool
 260       * @throws invalid_parameter_exception
 261       */
 262      public static function delete_report_condition(int $reportid, int $conditionid): bool {
 263          global $DB;
 264  
 265          $condition = filter::get_condition_record($reportid, $conditionid);
 266          if ($condition === false) {
 267              throw new invalid_parameter_exception('Invalid condition');
 268          }
 269  
 270          // After deletion, re-index remaining report conditions.
 271          if ($result = $condition->delete()) {
 272              $sqlupdateorder = '
 273                  UPDATE {' . filter::TABLE . '}
 274                     SET filterorder = filterorder - 1
 275                   WHERE reportid = :reportid
 276                     AND filterorder > :filterorder
 277                     AND iscondition = 1';
 278  
 279              $DB->execute($sqlupdateorder, ['reportid' => $reportid, 'filterorder' => $condition->get('filterorder')]);
 280          }
 281  
 282          return $result;
 283      }
 284  
 285      /**
 286       * Re-order given condition within a report
 287       *
 288       * @param int $reportid
 289       * @param int $conditionid
 290       * @param int $position
 291       * @return bool
 292       * @throws invalid_parameter_exception
 293       */
 294      public static function reorder_report_condition(int $reportid, int $conditionid, int $position): bool {
 295          $condition = filter::get_condition_record($reportid, $conditionid);
 296          if ($condition === false) {
 297              throw new invalid_parameter_exception('Invalid condition');
 298          }
 299  
 300          // Get the rest of the report conditions, excluding the one we are moving.
 301          $conditions = filter::get_records_select('reportid = :reportid AND iscondition = 1 AND id <> :id', [
 302              'reportid' => $reportid,
 303              'id' => $conditionid,
 304          ], 'filterorder');
 305  
 306          return static::reorder_persistents_by_field($condition, $conditions, $position, 'filterorder');
 307      }
 308  
 309      /**
 310       * Add given filter to report
 311       *
 312       * @param int $reportid
 313       * @param string $uniqueidentifier
 314       * @return filter
 315       * @throws invalid_parameter_exception
 316       */
 317      public static function add_report_filter(int $reportid, string $uniqueidentifier): filter {
 318          $report = manager::get_report_from_id($reportid);
 319  
 320          $reportfilters = $report->get_filters();
 321          if (!array_key_exists($uniqueidentifier, $reportfilters)) {
 322              throw new invalid_parameter_exception('Invalid filter');
 323          }
 324  
 325          $filter = new filter(0, (object) [
 326              'reportid' => $reportid,
 327              'uniqueidentifier' => $uniqueidentifier,
 328              'filterorder' => filter::get_max_filterorder($reportid) + 1,
 329          ]);
 330  
 331          return $filter->create();
 332      }
 333  
 334      /**
 335       * Delete given filter from report
 336       *
 337       * @param int $reportid
 338       * @param int $filterid
 339       * @return bool
 340       * @throws invalid_parameter_exception
 341       */
 342      public static function delete_report_filter(int $reportid, int $filterid): bool {
 343          global $DB;
 344  
 345          $filter = filter::get_filter_record($reportid, $filterid);
 346          if ($filter === false) {
 347              throw new invalid_parameter_exception('Invalid filter');
 348          }
 349  
 350          // After deletion, re-index remaining report filters.
 351          if ($result = $filter->delete()) {
 352              $sqlupdateorder = '
 353                  UPDATE {' . filter::TABLE . '}
 354                     SET filterorder = filterorder - 1
 355                   WHERE reportid = :reportid
 356                     AND filterorder > :filterorder
 357                     AND iscondition = 0';
 358  
 359              $DB->execute($sqlupdateorder, ['reportid' => $reportid, 'filterorder' => $filter->get('filterorder')]);
 360          }
 361  
 362          return $result;
 363      }
 364  
 365      /**
 366       * Re-order given filter within a report
 367       *
 368       * @param int $reportid
 369       * @param int $filterid
 370       * @param int $position
 371       * @return bool
 372       * @throws invalid_parameter_exception
 373       */
 374      public static function reorder_report_filter(int $reportid, int $filterid, int $position): bool {
 375          $filter = filter::get_filter_record($reportid, $filterid);
 376          if ($filter === false) {
 377              throw new invalid_parameter_exception('Invalid filter');
 378          }
 379  
 380          // Get the rest of the report filters, excluding the one we are moving.
 381          $filters = filter::get_records_select('reportid = :reportid AND iscondition = 0 AND id <> :id', [
 382              'reportid' => $reportid,
 383              'id' => $filterid,
 384          ], 'filterorder');
 385  
 386          return static::reorder_persistents_by_field($filter, $filters, $position, 'filterorder');
 387      }
 388  
 389      /**
 390       * Get available columns for a given report
 391       *
 392       * @param report_model $persistent
 393       * @return array
 394       */
 395      public static function get_available_columns(report_model $persistent) : array {
 396          $available = [];
 397  
 398          $report = manager::get_report_from_persistent($persistent);
 399  
 400          // Get current report columns.
 401          foreach ($report->get_columns() as $column) {
 402              $entityname = $column->get_entity_name();
 403              $entitytitle = $column->get_title();
 404              if (!array_key_exists($entityname, $available)) {
 405                  $available[$entityname] = [
 406                      'name' => (string) $report->get_entity_title($entityname),
 407                      'key' => $entityname,
 408                      'items' => [],
 409                  ];
 410              }
 411  
 412              $available[$entityname]['items'][] = [
 413                  'name' => $entitytitle,
 414                  'identifier' => $column->get_unique_identifier(),
 415                  'title' => get_string('addcolumn', 'core_reportbuilder', $entitytitle),
 416                  'action' => 'report-add-column'
 417              ];
 418          }
 419  
 420          return array_values($available);
 421      }
 422  
 423      /**
 424       * Helper method for re-ordering given persistents (columns, filters, etc)
 425       *
 426       * @param persistent $persistent The persistent we are moving
 427       * @param persistent[] $persistents The rest of the persistents
 428       * @param int $position
 429       * @param string $field The field we need to update
 430       * @return bool
 431       */
 432      private static function reorder_persistents_by_field(persistent $persistent, array $persistents, int $position,
 433              string $field): bool {
 434  
 435          // Splice into new position.
 436          array_splice($persistents, $position - 1, 0, [$persistent]);
 437  
 438          $fieldorder = 1;
 439          foreach ($persistents as $persistent) {
 440              $persistent->set($field, $fieldorder++)
 441                  ->update();
 442          }
 443  
 444          return true;
 445      }
 446  }