Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 400 and 403] [Versions 401 and 403] [Versions 402 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          // Ensure column is available.
 114          if (!array_key_exists($uniqueidentifier, $report->get_columns())) {
 115              throw new invalid_parameter_exception('Invalid column');
 116          }
 117  
 118          $column = new column(0, (object) [
 119              'reportid' => $reportid,
 120              'uniqueidentifier' => $uniqueidentifier,
 121              'columnorder' => column::get_max_columnorder($reportid, 'columnorder') + 1,
 122              'sortorder' => column::get_max_columnorder($reportid, 'sortorder') + 1,
 123          ]);
 124  
 125          return $column->create();
 126      }
 127  
 128      /**
 129       * Delete given column from report
 130       *
 131       * @param int $reportid
 132       * @param int $columnid
 133       * @return bool
 134       * @throws invalid_parameter_exception
 135       */
 136      public static function delete_report_column(int $reportid, int $columnid): bool {
 137          global $DB;
 138  
 139          $column = column::get_record(['id' => $columnid, 'reportid' => $reportid]);
 140          if ($column === false) {
 141              throw new invalid_parameter_exception('Invalid column');
 142          }
 143  
 144          // After deletion, re-index remaining report columns.
 145          if ($result = $column->delete()) {
 146              $sqlupdateorder = '
 147                  UPDATE {' . column::TABLE . '}
 148                     SET columnorder = columnorder - 1
 149                   WHERE reportid = :reportid
 150                     AND columnorder > :columnorder';
 151  
 152              $DB->execute($sqlupdateorder, ['reportid' => $reportid, 'columnorder' => $column->get('columnorder')]);
 153          }
 154  
 155          return $result;
 156      }
 157  
 158      /**
 159       * Re-order given column within a report
 160       *
 161       * @param int $reportid
 162       * @param int $columnid
 163       * @param int $position
 164       * @return bool
 165       * @throws invalid_parameter_exception
 166       */
 167      public static function reorder_report_column(int $reportid, int $columnid, int $position): bool {
 168          $column = column::get_record(['id' => $columnid, 'reportid' => $reportid]);
 169          if ($column === false) {
 170              throw new invalid_parameter_exception('Invalid column');
 171          }
 172  
 173          // Get the rest of the report columns, excluding the one we are moving.
 174          $columns = column::get_records_select('reportid = :reportid AND id <> :id', [
 175              'reportid' => $reportid,
 176              'id' => $columnid,
 177          ], 'columnorder');
 178  
 179          return static::reorder_persistents_by_field($column, $columns, $position, 'columnorder');
 180      }
 181  
 182      /**
 183       * Re-order given column sorting within a report
 184       *
 185       * @param int $reportid
 186       * @param int $columnid
 187       * @param int $position
 188       * @return bool
 189       * @throws invalid_parameter_exception
 190       */
 191      public static function reorder_report_column_sorting(int $reportid, int $columnid, int $position): bool {
 192          $column = column::get_record(['id' => $columnid, 'reportid' => $reportid]);
 193          if ($column === false) {
 194              throw new invalid_parameter_exception('Invalid column');
 195          }
 196  
 197          // Get the rest of the report columns, excluding the one we are moving.
 198          $columns = column::get_records_select('reportid = :reportid AND id <> :id', [
 199              'reportid' => $reportid,
 200              'id' => $columnid,
 201          ], 'sortorder');
 202  
 203          return static::reorder_persistents_by_field($column, $columns, $position, 'sortorder');
 204      }
 205  
 206      /**
 207       * Toggle sorting options for given column within a report
 208       *
 209       * @param int $reportid
 210       * @param int $columnid
 211       * @param bool $enabled
 212       * @param int $direction
 213       * @return bool
 214       * @throws invalid_parameter_exception
 215       */
 216      public static function toggle_report_column_sorting(int $reportid, int $columnid, bool $enabled,
 217              int $direction = SORT_ASC): bool {
 218  
 219          $column = column::get_record(['id' => $columnid, 'reportid' => $reportid]);
 220          if ($column === false) {
 221              throw new invalid_parameter_exception('Invalid column');
 222          }
 223  
 224          return $column->set_many([
 225              'sortenabled' => $enabled,
 226              'sortdirection' => $direction,
 227          ])->update();
 228      }
 229  
 230      /**
 231       * Add given condition to report
 232       *
 233       * @param int $reportid
 234       * @param string $uniqueidentifier
 235       * @return filter
 236       * @throws invalid_parameter_exception
 237       */
 238      public static function add_report_condition(int $reportid, string $uniqueidentifier): filter {
 239          $report = manager::get_report_from_id($reportid);
 240  
 241          // Ensure condition is available.
 242          if (!array_key_exists($uniqueidentifier, $report->get_conditions())) {
 243              throw new invalid_parameter_exception('Invalid condition');
 244          }
 245  
 246          // Ensure condition wasn't already added.
 247          if (array_key_exists($uniqueidentifier, $report->get_active_conditions())) {
 248              throw new invalid_parameter_exception('Duplicate condition');
 249          }
 250  
 251          $condition = new filter(0, (object) [
 252              'reportid' => $reportid,
 253              'uniqueidentifier' => $uniqueidentifier,
 254              'iscondition' => true,
 255              'filterorder' => filter::get_max_filterorder($reportid, true) + 1,
 256          ]);
 257  
 258          return $condition->create();
 259      }
 260  
 261      /**
 262       * Delete given condition from report
 263       *
 264       * @param int $reportid
 265       * @param int $conditionid
 266       * @return bool
 267       * @throws invalid_parameter_exception
 268       */
 269      public static function delete_report_condition(int $reportid, int $conditionid): bool {
 270          global $DB;
 271  
 272          $condition = filter::get_condition_record($reportid, $conditionid);
 273          if ($condition === false) {
 274              throw new invalid_parameter_exception('Invalid condition');
 275          }
 276  
 277          // After deletion, re-index remaining report conditions.
 278          if ($result = $condition->delete()) {
 279              $sqlupdateorder = '
 280                  UPDATE {' . filter::TABLE . '}
 281                     SET filterorder = filterorder - 1
 282                   WHERE reportid = :reportid
 283                     AND filterorder > :filterorder
 284                     AND iscondition = 1';
 285  
 286              $DB->execute($sqlupdateorder, ['reportid' => $reportid, 'filterorder' => $condition->get('filterorder')]);
 287          }
 288  
 289          return $result;
 290      }
 291  
 292      /**
 293       * Re-order given condition within a report
 294       *
 295       * @param int $reportid
 296       * @param int $conditionid
 297       * @param int $position
 298       * @return bool
 299       * @throws invalid_parameter_exception
 300       */
 301      public static function reorder_report_condition(int $reportid, int $conditionid, int $position): bool {
 302          $condition = filter::get_condition_record($reportid, $conditionid);
 303          if ($condition === false) {
 304              throw new invalid_parameter_exception('Invalid condition');
 305          }
 306  
 307          // Get the rest of the report conditions, excluding the one we are moving.
 308          $conditions = filter::get_records_select('reportid = :reportid AND iscondition = 1 AND id <> :id', [
 309              'reportid' => $reportid,
 310              'id' => $conditionid,
 311          ], 'filterorder');
 312  
 313          return static::reorder_persistents_by_field($condition, $conditions, $position, 'filterorder');
 314      }
 315  
 316      /**
 317       * Add given filter to report
 318       *
 319       * @param int $reportid
 320       * @param string $uniqueidentifier
 321       * @return filter
 322       * @throws invalid_parameter_exception
 323       */
 324      public static function add_report_filter(int $reportid, string $uniqueidentifier): filter {
 325          $report = manager::get_report_from_id($reportid);
 326  
 327          // Ensure filter is available.
 328          if (!array_key_exists($uniqueidentifier, $report->get_filters())) {
 329              throw new invalid_parameter_exception('Invalid filter');
 330          }
 331  
 332          // Ensure filter wasn't already added.
 333          if (array_key_exists($uniqueidentifier, $report->get_active_filters())) {
 334              throw new invalid_parameter_exception('Duplicate filter');
 335          }
 336  
 337          $filter = new filter(0, (object) [
 338              'reportid' => $reportid,
 339              'uniqueidentifier' => $uniqueidentifier,
 340              'filterorder' => filter::get_max_filterorder($reportid) + 1,
 341          ]);
 342  
 343          return $filter->create();
 344      }
 345  
 346      /**
 347       * Delete given filter from report
 348       *
 349       * @param int $reportid
 350       * @param int $filterid
 351       * @return bool
 352       * @throws invalid_parameter_exception
 353       */
 354      public static function delete_report_filter(int $reportid, int $filterid): bool {
 355          global $DB;
 356  
 357          $filter = filter::get_filter_record($reportid, $filterid);
 358          if ($filter === false) {
 359              throw new invalid_parameter_exception('Invalid filter');
 360          }
 361  
 362          // After deletion, re-index remaining report filters.
 363          if ($result = $filter->delete()) {
 364              $sqlupdateorder = '
 365                  UPDATE {' . filter::TABLE . '}
 366                     SET filterorder = filterorder - 1
 367                   WHERE reportid = :reportid
 368                     AND filterorder > :filterorder
 369                     AND iscondition = 0';
 370  
 371              $DB->execute($sqlupdateorder, ['reportid' => $reportid, 'filterorder' => $filter->get('filterorder')]);
 372          }
 373  
 374          return $result;
 375      }
 376  
 377      /**
 378       * Re-order given filter within a report
 379       *
 380       * @param int $reportid
 381       * @param int $filterid
 382       * @param int $position
 383       * @return bool
 384       * @throws invalid_parameter_exception
 385       */
 386      public static function reorder_report_filter(int $reportid, int $filterid, int $position): bool {
 387          $filter = filter::get_filter_record($reportid, $filterid);
 388          if ($filter === false) {
 389              throw new invalid_parameter_exception('Invalid filter');
 390          }
 391  
 392          // Get the rest of the report filters, excluding the one we are moving.
 393          $filters = filter::get_records_select('reportid = :reportid AND iscondition = 0 AND id <> :id', [
 394              'reportid' => $reportid,
 395              'id' => $filterid,
 396          ], 'filterorder');
 397  
 398          return static::reorder_persistents_by_field($filter, $filters, $position, 'filterorder');
 399      }
 400  
 401      /**
 402       * Get available columns for a given report
 403       *
 404       * @param report_model $persistent
 405       * @return array
 406       *
 407       * @deprecated since Moodle 4.1 - please do not use this function any more, {@see custom_report_column_cards_exporter}
 408       */
 409      public static function get_available_columns(report_model $persistent) : array {
 410          debugging('The function ' . __FUNCTION__ . '() is deprecated, please do not use it any more. ' .
 411              'See \'custom_report_column_cards_exporter\' class for replacement', DEBUG_DEVELOPER);
 412  
 413          $available = [];
 414  
 415          $report = manager::get_report_from_persistent($persistent);
 416  
 417          // Get current report columns.
 418          foreach ($report->get_columns() as $column) {
 419              $entityname = $column->get_entity_name();
 420              $entitytitle = $column->get_title();
 421              if (!array_key_exists($entityname, $available)) {
 422                  $available[$entityname] = [
 423                      'name' => (string) $report->get_entity_title($entityname),
 424                      'key' => $entityname,
 425                      'items' => [],
 426                  ];
 427              }
 428  
 429              $available[$entityname]['items'][] = [
 430                  'name' => $entitytitle,
 431                  'identifier' => $column->get_unique_identifier(),
 432                  'title' => get_string('addcolumn', 'core_reportbuilder', $entitytitle),
 433                  'action' => 'report-add-column'
 434              ];
 435          }
 436  
 437          return array_values($available);
 438      }
 439  
 440      /**
 441       * Helper method for re-ordering given persistents (columns, filters, etc)
 442       *
 443       * @param persistent $persistent The persistent we are moving
 444       * @param persistent[] $persistents The rest of the persistents
 445       * @param int $position
 446       * @param string $field The field we need to update
 447       * @return bool
 448       */
 449      private static function reorder_persistents_by_field(persistent $persistent, array $persistents, int $position,
 450              string $field): bool {
 451  
 452          // Splice into new position.
 453          array_splice($persistents, $position - 1, 0, [$persistent]);
 454  
 455          $fieldorder = 1;
 456          foreach ($persistents as $persistent) {
 457              $persistent->set($field, $fieldorder++)
 458                  ->update();
 459          }
 460  
 461          return true;
 462      }
 463  }