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\entities;
  20  
  21  use coding_exception;
  22  use lang_string;
  23  use core_reportbuilder\local\report\column;
  24  use core_reportbuilder\local\report\filter;
  25  
  26  /**
  27   * Base class for all report entities
  28   *
  29   * @package     core_reportbuilder
  30   * @copyright   2019 Marina Glancy <marina@moodle.com>
  31   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  32   */
  33  abstract class base {
  34  
  35      /** @var string $entityname Internal reference to name of entity */
  36      private $entityname = null;
  37  
  38      /** @var lang_string $entitytitle Used as a title for the entity in reports */
  39      private $entitytitle = null;
  40  
  41      /** @var array $tablealiases Database tables that this entity uses and their aliases */
  42      private $tablealiases = [];
  43  
  44      /** @var array $tablejoinaliases Database tables that have already been joined to the report and their aliases */
  45      private $tablejoinaliases = [];
  46  
  47      /** @var string[] $joins List of SQL joins for the entity */
  48      private $joins = [];
  49  
  50      /** @var column[] $columns List of columns for the entity */
  51      private $columns = [];
  52  
  53      /** @var filter[] $filters List of filters for the entity */
  54      private $filters = [];
  55  
  56      /** @var filter[] $conditions List of conditions for the entity */
  57      private $conditions = [];
  58  
  59      /**
  60       * Database tables that this entity uses and their default aliases
  61       *
  62       * Must be overridden by the entity to list all database tables that it expects to be present in the main
  63       * SQL or in JOINs added to this entity
  64       *
  65       * @return string[] Array of $tablename => $alias
  66       */
  67      abstract protected function get_default_table_aliases(): array;
  68  
  69      /**
  70       * The default title for this entity
  71       *
  72       * @return lang_string
  73       */
  74      abstract protected function get_default_entity_title(): lang_string;
  75  
  76      /**
  77       * Initialise the entity, called automatically when it is added to a report
  78       *
  79       * This is where entity defines all its columns and filters by calling:
  80       * - {@see add_column}
  81       * - {@see add_filter}
  82       * - etc
  83       *
  84       * @return self
  85       */
  86      abstract public function initialise(): self;
  87  
  88      /**
  89       * The default machine-readable name for this entity that will be used in the internal names of the columns/filters
  90       *
  91       * @return string
  92       */
  93      private function get_default_entity_name(): string {
  94          $namespace = explode('\\', get_called_class());
  95  
  96          return end($namespace);
  97      }
  98  
  99      /**
 100       * Set entity name
 101       *
 102       * @param string $entityname
 103       * @return self
 104       */
 105      final public function set_entity_name(string $entityname): self {
 106          $this->entityname = $entityname;
 107          return $this;
 108      }
 109  
 110      /**
 111       * Return entity name
 112       *
 113       * @return string
 114       */
 115      final public function get_entity_name(): string {
 116          return $this->entityname ?? $this->get_default_entity_name();
 117      }
 118  
 119      /**
 120       * Set entity title
 121       *
 122       * @param lang_string $title
 123       * @return self
 124       */
 125      final public function set_entity_title(lang_string $title): self {
 126          $this->entitytitle = $title;
 127          return $this;
 128      }
 129  
 130      /**
 131       * Get entity title
 132       *
 133       * @return lang_string
 134       */
 135      final public function get_entity_title(): lang_string {
 136          return $this->entitytitle ?? $this->get_default_entity_title();
 137      }
 138  
 139      /**
 140       * Override the default alias for given database table used in entity queries, to avoid table alias clashes that may occur
 141       * if multiple entities of a report each define the same default alias for one of their tables
 142       *
 143       * @param string $tablename
 144       * @param string $alias
 145       * @return self
 146       * @throws coding_exception
 147       */
 148      final public function set_table_alias(string $tablename, string $alias): self {
 149          if (!array_key_exists($tablename, $this->get_default_table_aliases())) {
 150              throw new coding_exception('Invalid table name', $tablename);
 151          }
 152  
 153          $this->tablealiases[$tablename] = $alias;
 154          return $this;
 155      }
 156  
 157      /**
 158       * Override multiple default database table aliases used in entity queries as per {@see set_table_alias}, typically when
 159       * you're adding an entity multiple times to a report you'd want to override the table aliases in the second instance to
 160       * avoid clashes with the first
 161       *
 162       * @param array $aliases Array of tablename => alias values
 163       * @return self
 164       */
 165      final public function set_table_aliases(array $aliases): self {
 166          foreach ($aliases as $tablename => $alias) {
 167              $this->set_table_alias($tablename, $alias);
 168          }
 169          return $this;
 170      }
 171  
 172      /**
 173       * Returns an alias used in the queries for a given table
 174       *
 175       * @param string $tablename
 176       * @return string
 177       * @throws coding_exception
 178       */
 179      final public function get_table_alias(string $tablename): string {
 180          $defaulttablealiases = $this->get_default_table_aliases();
 181          if (!array_key_exists($tablename, $defaulttablealiases)) {
 182              throw new coding_exception('Invalid table name', $tablename);
 183          }
 184  
 185          return $this->tablealiases[$tablename] ?? $defaulttablealiases[$tablename];
 186      }
 187  
 188      /**
 189       * Set the alias for given database table that has already been added to the report. Enables entities to avoid additional
 190       * joins on the same table by allowing re-use of existing table aliases in their own queries, {@see has_table_join_alias}
 191       *
 192       * @param string $tablename
 193       * @param string $alias
 194       * @return self
 195       */
 196      final public function set_table_join_alias(string $tablename, string $alias): self {
 197          $this->tablejoinaliases[$tablename] = $alias;
 198  
 199          // Internally set the same table alias for the entity.
 200          return $this->set_table_alias($tablename, $alias);
 201      }
 202  
 203      /**
 204       * Determine whether defined table join alias was specified. Call {@see get_table_alias} to retrieve said value
 205       *
 206       * @param string $tablename
 207       * @return bool
 208       */
 209      final public function has_table_join_alias(string $tablename): bool {
 210          return array_key_exists($tablename, $this->tablejoinaliases);
 211      }
 212  
 213      /**
 214       * Add join clause required for this entity to join to existing tables/entities
 215       *
 216       * @param string $join
 217       * @return self
 218       */
 219      final public function add_join(string $join): self {
 220          $this->joins[trim($join)] = trim($join);
 221          return $this;
 222      }
 223  
 224      /**
 225       * Add multiple join clauses required for this entity {@see add_join}
 226       *
 227       * @param string[] $joins
 228       * @return self
 229       */
 230      final public function add_joins(array $joins): self {
 231          foreach ($joins as $join) {
 232              $this->add_join($join);
 233          }
 234          return $this;
 235      }
 236  
 237      /**
 238       * Return entity joins
 239       *
 240       * @return string[]
 241       */
 242      final public function get_joins(): array {
 243          return array_values($this->joins);
 244      }
 245  
 246      /**
 247       * Helper method for returning joins necessary for retrieving tags related to the current entity
 248       *
 249       * Both 'tag' and 'tag_instance' aliases must be returned by the entity {@see get_default_table_aliases} method
 250       *
 251       * @param string $component
 252       * @param string $itemtype
 253       * @param string $itemidfield
 254       * @return string[]
 255       */
 256      final protected function get_tag_joins_for_entity(string $component, string $itemtype, string $itemidfield): array {
 257          $taginstancealias = $this->get_table_alias('tag_instance');
 258          $tagalias = $this->get_table_alias('tag');
 259  
 260          return [
 261              "LEFT JOIN {tag_instance} {$taginstancealias}
 262                      ON {$taginstancealias}.component = '{$component}'
 263                     AND {$taginstancealias}.itemtype = '{$itemtype}'
 264                     AND {$taginstancealias}.itemid = {$itemidfield}",
 265              "LEFT JOIN {tag} {$tagalias}
 266                      ON {$tagalias}.id = {$taginstancealias}.tagid",
 267          ];
 268      }
 269  
 270      /**
 271       * Add a column to the entity
 272       *
 273       * @param column $column
 274       * @return self
 275       */
 276      final protected function add_column(column $column): self {
 277          $this->columns[$column->get_name()] = $column;
 278          return $this;
 279      }
 280  
 281      /**
 282       * Returns entity columns
 283       *
 284       * @return column[]
 285       */
 286      final public function get_columns(): array {
 287          return $this->columns;
 288      }
 289  
 290      /**
 291       * Returns an entity column
 292       *
 293       * @param string $name
 294       * @return column
 295       * @throws coding_exception For invalid column name
 296       */
 297      final public function get_column(string $name): column {
 298          if (!array_key_exists($name, $this->columns)) {
 299              throw new coding_exception('Invalid column name', $name);
 300          }
 301  
 302          return $this->columns[$name];
 303      }
 304  
 305      /**
 306       * Add a filter to the entity
 307       *
 308       * @param filter $filter
 309       * @return self
 310       */
 311      final protected function add_filter(filter $filter): self {
 312          $this->filters[$filter->get_name()] = $filter;
 313          return $this;
 314      }
 315  
 316      /**
 317       * Returns entity filters
 318       *
 319       * @return filter[]
 320       */
 321      final public function get_filters(): array {
 322          return $this->filters;
 323      }
 324  
 325      /**
 326       * Returns an entity filter
 327       *
 328       * @param string $name
 329       * @return filter
 330       * @throws coding_exception For invalid filter name
 331       */
 332      final public function get_filter(string $name): filter {
 333          if (!array_key_exists($name, $this->filters)) {
 334              throw new coding_exception('Invalid filter name', $name);
 335          }
 336  
 337          return $this->filters[$name];
 338      }
 339  
 340      /**
 341       * Add a condition to the entity
 342       *
 343       * @param filter $condition
 344       * @return $this
 345       */
 346      final protected function add_condition(filter $condition): self {
 347          $this->conditions[$condition->get_name()] = $condition;
 348          return $this;
 349      }
 350  
 351      /**
 352       * Returns entity conditions
 353       *
 354       * @return filter[]
 355       */
 356      final public function get_conditions(): array {
 357          return $this->conditions;
 358      }
 359  
 360      /**
 361       * Returns an entity condition
 362       *
 363       * @param string $name
 364       * @return filter
 365       * @throws coding_exception For invalid condition name
 366       */
 367      final public function get_condition(string $name): filter {
 368          if (!array_key_exists($name, $this->conditions)) {
 369              throw new coding_exception('Invalid condition name', $name);
 370          }
 371  
 372          return $this->conditions[$name];
 373      }
 374  }