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; 20 21 use coding_exception; 22 use core_reportbuilder\local\helpers\report; 23 use core_reportbuilder\local\models\column as column_model; 24 use core_reportbuilder\local\models\filter as filter_model; 25 use core_reportbuilder\local\report\base; 26 use core_reportbuilder\local\report\column; 27 use core_reportbuilder\local\report\filter; 28 29 /** 30 * Class datasource 31 * 32 * @package core_reportbuilder 33 * @copyright 2021 David Matamoros <davidmc@moodle.com> 34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 */ 36 abstract class datasource extends base { 37 38 /** @var float[] $elementsmodified Track the time elements of specific reports have been added, updated, removed */ 39 private static $elementsmodified = []; 40 41 /** @var array $activecolumns */ 42 private $activecolumns; 43 44 /** @var array $activefilters */ 45 private $activefilters; 46 47 /** @var array $activeconditions */ 48 private $activeconditions; 49 50 /** 51 * Return user friendly name of the datasource 52 * 53 * @return string 54 */ 55 abstract public static function get_name(): string; 56 57 /** 58 * Add columns from the given entity name to be available to use in a custom report 59 * 60 * @param string $entityname 61 * @param array $include Include only these columns, if omitted then include all 62 * @param array $exclude Exclude these columns, if omitted then exclude none 63 * @throws coding_exception If both $include and $exclude are non-empty 64 */ 65 final protected function add_columns_from_entity(string $entityname, array $include = [], array $exclude = []): void { 66 if (!empty($include) && !empty($exclude)) { 67 throw new coding_exception('Cannot specify columns to include and exclude simultaneously'); 68 } 69 70 $entity = $this->get_entity($entityname); 71 72 // Retrieve filtered columns from entity, respecting given $include/$exclude parameters. 73 $columns = array_filter($entity->get_columns(), static function(column $column) use ($include, $exclude): bool { 74 if (!empty($include)) { 75 return in_array($column->get_name(), $include); 76 } 77 78 if (!empty($exclude)) { 79 return !in_array($column->get_name(), $exclude); 80 } 81 82 return true; 83 }); 84 85 foreach ($columns as $column) { 86 $this->add_column($column); 87 } 88 } 89 90 /** 91 * Add default datasource columns to the report 92 * 93 * Uses column data returned by the source {@see get_default_columns} and {@see get_default_column_sorting} methods 94 * 95 * @throws coding_exception If default column sorting refers to an invalid column 96 */ 97 public function add_default_columns(): void { 98 $reportid = $this->get_report_persistent()->get('id'); 99 100 // Retrieve default column sorting, and track index of both sorted/non-sorted columns. 101 $columnidentifiers = $this->get_default_columns(); 102 103 $defaultcolumnsorting = $this->get_default_column_sorting(); 104 $defaultcolumnsortinginvalid = array_diff_key($defaultcolumnsorting, 105 array_fill_keys($columnidentifiers, 1)); 106 107 if (count($defaultcolumnsortinginvalid) > 0) { 108 throw new coding_exception('Invalid column name', array_key_first($defaultcolumnsortinginvalid)); 109 } 110 111 $columnnonsortingindex = count($defaultcolumnsorting) + 1; 112 113 foreach ($columnidentifiers as $uniqueidentifier) { 114 $column = report::add_report_column($reportid, $uniqueidentifier); 115 116 // After adding the column, toggle sorting according to defaults provided by the datasource. 117 $sortorder = array_search($uniqueidentifier, array_keys($defaultcolumnsorting)); 118 if ($sortorder !== false) { 119 $column->set_many([ 120 'sortenabled' => true, 121 'sortdirection' => $defaultcolumnsorting[$uniqueidentifier], 122 'sortorder' => $sortorder + 1, 123 ])->update(); 124 } else if (!empty($defaultcolumnsorting)) { 125 $column->set('sortorder', $columnnonsortingindex++)->update(); 126 } 127 } 128 } 129 130 /** 131 * Return the default columns that will be added to the report upon creation, by {@see add_default_columns} 132 * 133 * @return string[] 134 */ 135 abstract public function get_default_columns(): array; 136 137 /** 138 * Return the default column sorting that will be set for the report upon creation, by {@see add_default_columns} 139 * 140 * When overriding this method in child classes, column identifiers specified must refer to default columns returned from 141 * the {@see get_default_columns} method 142 * 143 * @return int[] array [column identifier => SORT_ASC/SORT_DESC] 144 */ 145 public function get_default_column_sorting(): array { 146 return []; 147 } 148 149 /** 150 * Override parent method, returning only those columns specifically added to the custom report (rather than all that are 151 * available) 152 * 153 * @return column[] 154 */ 155 public function get_active_columns(): array { 156 $reportid = $this->get_report_persistent()->get('id'); 157 158 // Determine whether we already retrieved the columns since the report was last modified. 159 self::$elementsmodified += [$reportid => -1]; 160 if ($this->activecolumns !== null && $this->activecolumns['builttime'] > self::$elementsmodified[$reportid]) { 161 return $this->activecolumns['values']; 162 } 163 164 $this->activecolumns = ['builttime' => microtime(true), 'values' => []]; 165 166 $activecolumns = column_model::get_records(['reportid' => $reportid], 'columnorder'); 167 foreach ($activecolumns as $index => $column) { 168 $instance = $this->get_column($column->get('uniqueidentifier')); 169 170 // Ensure the column is still present and available. 171 if ($instance !== null && $instance->get_is_available()) { 172 if ($instance->get_is_deprecated()) { 173 debugging("The column '{$instance->get_unique_identifier()}' is deprecated, please do not use it any more." . 174 " {$instance->get_is_deprecated_message()}", DEBUG_DEVELOPER); 175 } 176 177 // We should clone the report column to ensure if it's added twice to a report, each operates independently. 178 $this->activecolumns['values'][] = clone $instance 179 ->set_index($index) 180 ->set_persistent($column) 181 ->set_aggregation($column->get('aggregation')); 182 } 183 } 184 185 return $this->activecolumns['values']; 186 } 187 188 /** 189 * Add filters from the given entity name to be available to use in a custom report 190 * 191 * @param string $entityname 192 * @param array $include Include only these filters, if omitted then include all 193 * @param array $exclude Exclude these filters, if omitted then exclude none 194 * @throws coding_exception If both $include and $exclude are non-empty 195 */ 196 final protected function add_filters_from_entity(string $entityname, array $include = [], array $exclude = []): void { 197 if (!empty($include) && !empty($exclude)) { 198 throw new coding_exception('Cannot specify filters to include and exclude simultaneously'); 199 } 200 201 $entity = $this->get_entity($entityname); 202 203 // Retrieve filtered filters from entity, respecting given $include/$exclude parameters. 204 $filters = array_filter($entity->get_filters(), static function(filter $filter) use ($include, $exclude): bool { 205 if (!empty($include)) { 206 return in_array($filter->get_name(), $include); 207 } 208 209 if (!empty($exclude)) { 210 return !in_array($filter->get_name(), $exclude); 211 } 212 213 return true; 214 }); 215 216 foreach ($filters as $filter) { 217 $this->add_filter($filter); 218 } 219 } 220 221 /** 222 * Add default datasource filters to the report 223 * 224 * This method is optional and can be called when the report is created to add the default filters defined in the 225 * selected datasource. 226 */ 227 public function add_default_filters(): void { 228 $reportid = $this->get_report_persistent()->get('id'); 229 $filteridentifiers = $this->get_default_filters(); 230 foreach ($filteridentifiers as $uniqueidentifier) { 231 report::add_report_filter($reportid, $uniqueidentifier); 232 } 233 } 234 235 /** 236 * Return the filters that will be added to the report once is created 237 * 238 * @return string[] 239 */ 240 abstract public function get_default_filters(): array; 241 242 /** 243 * Override parent method, returning only those filters specifically added to the custom report (rather than all that are 244 * available) 245 * 246 * @return filter[] 247 */ 248 public function get_active_filters(): array { 249 $reportid = $this->get_report_persistent()->get('id'); 250 251 // Determine whether we already retrieved the filters since the report was last modified. 252 self::$elementsmodified += [$reportid => -1]; 253 if ($this->activefilters !== null && $this->activefilters['builttime'] > self::$elementsmodified[$reportid]) { 254 return $this->activefilters['values']; 255 } 256 257 $this->activefilters = ['builttime' => microtime(true), 'values' => []]; 258 259 $activefilters = filter_model::get_filter_records($reportid, 'filterorder'); 260 foreach ($activefilters as $filter) { 261 $instance = $this->get_filter($filter->get('uniqueidentifier')); 262 263 // Ensure the filter is still present and available. 264 if ($instance !== null && $instance->get_is_available()) { 265 if ($instance->get_is_deprecated()) { 266 debugging("The filter '{$instance->get_unique_identifier()}' is deprecated, please do not use it any more." . 267 " {$instance->get_is_deprecated_message()}", DEBUG_DEVELOPER); 268 } 269 270 $this->activefilters['values'][$instance->get_unique_identifier()] = 271 $instance->set_persistent($filter); 272 } 273 } 274 275 return $this->activefilters['values']; 276 } 277 278 /** 279 * Add conditions from the given entity name to be available to use in a custom report 280 * 281 * @param string $entityname 282 * @param array $include Include only these conditions, if omitted then include all 283 * @param array $exclude Exclude these conditions, if omitted then exclude none 284 * @throws coding_exception If both $include and $exclude are non-empty 285 */ 286 final protected function add_conditions_from_entity(string $entityname, array $include = [], array $exclude = []): void { 287 if (!empty($include) && !empty($exclude)) { 288 throw new coding_exception('Cannot specify conditions to include and exclude simultaneously'); 289 } 290 291 $entity = $this->get_entity($entityname); 292 293 // Retrieve filtered conditions from entity, respecting given $include/$exclude parameters. 294 $conditions = array_filter($entity->get_conditions(), static function(filter $condition) use ($include, $exclude): bool { 295 if (!empty($include)) { 296 return in_array($condition->get_name(), $include); 297 } 298 299 if (!empty($exclude)) { 300 return !in_array($condition->get_name(), $exclude); 301 } 302 303 return true; 304 }); 305 306 foreach ($conditions as $condition) { 307 $this->add_condition($condition); 308 } 309 } 310 311 /** 312 * Add default datasource conditions to the report 313 * 314 * This method is optional and can be called when the report is created to add the default conditions defined in the 315 * selected datasource. 316 */ 317 public function add_default_conditions(): void { 318 $reportid = $this->get_report_persistent()->get('id'); 319 $conditionidentifiers = $this->get_default_conditions(); 320 foreach ($conditionidentifiers as $uniqueidentifier) { 321 report::add_report_condition($reportid, $uniqueidentifier); 322 } 323 324 // Set the default condition values if they have been set in the datasource. 325 $this->set_condition_values($this->get_default_condition_values()); 326 } 327 328 /** 329 * Return the conditions that will be added to the report once is created 330 * 331 * @return string[] 332 */ 333 abstract public function get_default_conditions(): array; 334 335 /** 336 * Return the default condition values that will be added to the report once is created 337 * 338 * For any of the default conditions returned by the method {@see get_default_conditions} is 339 * possible to set the initial values. 340 * 341 * @return array 342 */ 343 public function get_default_condition_values(): array { 344 return []; 345 } 346 347 /** 348 * Override parent method, returning only those conditions specifically added to the custom report (rather than all that are 349 * available) 350 * 351 * @return filter[] 352 */ 353 public function get_active_conditions(): array { 354 $reportid = $this->get_report_persistent()->get('id'); 355 356 // Determine whether we already retrieved the conditions since the report was last modified. 357 self::$elementsmodified += [$reportid => -1]; 358 if ($this->activeconditions !== null && $this->activeconditions['builttime'] > self::$elementsmodified[$reportid]) { 359 return $this->activeconditions['values']; 360 } 361 362 $this->activeconditions = ['builttime' => microtime(true), 'values' => []]; 363 364 $activeconditions = filter_model::get_condition_records($reportid, 'filterorder'); 365 foreach ($activeconditions as $condition) { 366 $instance = $this->get_condition($condition->get('uniqueidentifier')); 367 368 // Ensure the condition is still present and available. 369 if ($instance !== null && $instance->get_is_available()) { 370 if ($instance->get_is_deprecated()) { 371 debugging("The condition '{$instance->get_unique_identifier()}' is deprecated, please do not use it any more." . 372 " {$instance->get_is_deprecated_message()}", DEBUG_DEVELOPER); 373 } 374 375 $this->activeconditions['values'][$instance->get_unique_identifier()] = 376 $instance->set_persistent($condition); 377 } 378 } 379 380 return $this->activeconditions['values']; 381 } 382 383 /** 384 * Adds all columns/filters/conditions from the given entity to the report at once 385 * 386 * @param string $entityname 387 */ 388 final protected function add_all_from_entity(string $entityname): void { 389 $this->add_columns_from_entity($entityname); 390 $this->add_filters_from_entity($entityname); 391 $this->add_conditions_from_entity($entityname); 392 } 393 394 /** 395 * Adds all columns/filters/conditions from all the entities added to the report at once 396 */ 397 final protected function add_all_from_entities(): void { 398 foreach ($this->get_entities() as $entity) { 399 $this->add_all_from_entity($entity->get_entity_name()); 400 } 401 } 402 403 /** 404 * Indicate that report elements have been modified, e.g. columns/filters/conditions have been added, removed or updated 405 * 406 * @param int $reportid 407 */ 408 final public static function report_elements_modified(int $reportid): void { 409 self::$elementsmodified[$reportid] = microtime(true); 410 } 411 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body