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\report; 20 21 use lang_string; 22 use moodle_exception; 23 use core_reportbuilder\local\filters\base; 24 use core_reportbuilder\local\helpers\database; 25 use core_reportbuilder\local\models\filter as filter_model; 26 27 /** 28 * Class to represent a report filter 29 * 30 * @package core_reportbuilder 31 * @copyright 2021 Paul Holden <paulh@moodle.com> 32 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 33 */ 34 final class filter { 35 36 /** @var string $filterclass */ 37 private $filterclass; 38 39 /** @var string $name */ 40 private $name; 41 42 /** @var lang_string $header */ 43 private $header; 44 45 /** @var string $entity */ 46 private $entityname; 47 48 /** @var string $fieldsql */ 49 private $fieldsql = ''; 50 51 /** @var array $fieldparams */ 52 private $fieldparams = []; 53 54 /** @var string[] $joins */ 55 protected $joins = []; 56 57 /** @var bool $available */ 58 protected $available = true; 59 60 /** @var bool $deprecated */ 61 protected $deprecated = false; 62 63 /** @var string $deprecatedmessage */ 64 protected $deprecatedmessage; 65 66 /** @var mixed $options */ 67 protected $options; 68 69 /** @var array $limitoperators */ 70 protected $limitoperators = []; 71 72 /** @var filter_model $persistent */ 73 protected $persistent; 74 75 /** 76 * Filter constructor 77 * 78 * @param string $filterclass Filter type class to use, must extend {@see base} filter class 79 * @param string $name Internal name of the filter 80 * @param lang_string $header Title of the filter used in reports 81 * @param string $entityname Name of the entity this filter belongs to. Typically when creating filters within entities 82 * this value should be the result of calling {@see get_entity_name}, however if creating filters inside reports directly 83 * it should be the name of the entity as passed to {@see \core_reportbuilder\local\report\base::annotate_entity} 84 * @param string $fieldsql SQL clause to use for filtering, {@see set_field_sql} 85 * @param array $fieldparams 86 * @throws moodle_exception For invalid filter class 87 */ 88 public function __construct( 89 string $filterclass, 90 string $name, 91 lang_string $header, 92 string $entityname, 93 string $fieldsql = '', 94 array $fieldparams = [] 95 ) { 96 if (!class_exists($filterclass) || !is_subclass_of($filterclass, base::class)) { 97 throw new moodle_exception('filterinvalid', 'reportbuilder', '', null, $filterclass); 98 } 99 100 $this->filterclass = $filterclass; 101 $this->name = $name; 102 $this->header = $header; 103 $this->entityname = $entityname; 104 105 if ($fieldsql !== '') { 106 $this->set_field_sql($fieldsql, $fieldparams); 107 } 108 } 109 110 /** 111 * Get filter class path 112 * 113 * @return string 114 */ 115 public function get_filter_class(): string { 116 return $this->filterclass; 117 } 118 119 /** 120 * Get filter name 121 * 122 * @return string 123 */ 124 public function get_name(): string { 125 return $this->name; 126 } 127 128 /** 129 * Return header 130 * 131 * @return string 132 */ 133 public function get_header(): string { 134 return $this->header->out(); 135 } 136 137 /** 138 * Set header 139 * 140 * @param lang_string $header 141 * @return self 142 */ 143 public function set_header(lang_string $header): self { 144 $this->header = $header; 145 return $this; 146 } 147 148 /** 149 * Return filter entity name 150 * 151 * @return string 152 */ 153 public function get_entity_name(): string { 154 return $this->entityname; 155 } 156 157 /** 158 * Return unique identifier for this filter 159 * 160 * @return string 161 */ 162 public function get_unique_identifier(): string { 163 return $this->get_entity_name() . ':' . $this->get_name(); 164 } 165 166 /** 167 * Return joins 168 * 169 * @return string[] 170 */ 171 public function get_joins(): array { 172 return array_values($this->joins); 173 } 174 175 /** 176 * Add join clause required for this filter to join to existing tables/entities 177 * 178 * This is necessary in the case where {@see set_field_sql} is selecting data from a table that isn't otherwise queried 179 * 180 * @param string $join 181 * @return self 182 */ 183 public function add_join(string $join): self { 184 $this->joins[trim($join)] = trim($join); 185 return $this; 186 } 187 188 /** 189 * Add multiple join clauses required for this filter, passing each to {@see add_join} 190 * 191 * Typically when defining filters in entities, you should pass {@see \core_reportbuilder\local\report\base::get_joins} to 192 * this method, so that all entity joins are included in the report when your filter is used in it 193 * 194 * @param string[] $joins 195 * @return self 196 */ 197 public function add_joins(array $joins): self { 198 foreach ($joins as $join) { 199 $this->add_join($join); 200 } 201 return $this; 202 } 203 204 /** 205 * Get SQL expression for the field 206 * 207 * @return string 208 */ 209 public function get_field_sql(): string { 210 return $this->fieldsql; 211 } 212 213 /** 214 * Get the SQL params for the field being filtered 215 * 216 * @return array 217 */ 218 public function get_field_params(): array { 219 return $this->fieldparams; 220 } 221 222 /** 223 * Retrieve SQL expression and parameters for the field 224 * 225 * @param int $index 226 * @return array [$sql, [...$params]] 227 */ 228 public function get_field_sql_and_params(int $index = 0): array { 229 $fieldsql = $this->get_field_sql(); 230 $fieldparams = $this->get_field_params(); 231 232 // Shortcut if there aren't any parameters. 233 if (empty($fieldparams)) { 234 return [$fieldsql, $fieldparams]; 235 } 236 237 // Simple callback for replacement of parameter names within filter SQL. 238 $transform = function(string $param) use ($index): string { 239 return "{$param}_{$index}"; 240 }; 241 242 $paramnames = array_keys($fieldparams); 243 $sql = database::sql_replace_parameter_names($fieldsql, $paramnames, $transform); 244 245 $params = []; 246 foreach ($paramnames as $paramname) { 247 $paramnametransform = $transform($paramname); 248 $params[$paramnametransform] = $fieldparams[$paramname]; 249 } 250 251 return [$sql, $params]; 252 } 253 254 /** 255 * Set the SQL expression for the field that is being filtered. It will be passed to the filter class 256 * 257 * @param string $sql 258 * @param array $params 259 * @return self 260 */ 261 public function set_field_sql(string $sql, array $params = []): self { 262 $this->fieldsql = $sql; 263 $this->fieldparams = $params; 264 return $this; 265 } 266 267 /** 268 * Return available state of the filter for the current user 269 * 270 * @return bool 271 */ 272 public function get_is_available(): bool { 273 return $this->available; 274 } 275 276 /** 277 * Conditionally set whether the filter is available. For instance the filter may be added to a report with the 278 * expectation that only some users are able to see it 279 * 280 * @param bool $available 281 * @return self 282 */ 283 public function set_is_available(bool $available): self { 284 $this->available = $available; 285 return $this; 286 } 287 288 /** 289 * Set deprecated state of the filter, in which case it will still be shown when already present in existing reports but 290 * won't be available for selection in the report editor 291 * 292 * @param string $deprecatedmessage 293 * @return self 294 */ 295 public function set_is_deprecated(string $deprecatedmessage = ''): self { 296 $this->deprecated = true; 297 $this->deprecatedmessage = $deprecatedmessage; 298 return $this; 299 } 300 301 /** 302 * Return deprecated state of the filter 303 * 304 * @return bool 305 */ 306 public function get_is_deprecated(): bool { 307 return $this->deprecated; 308 } 309 310 /** 311 * Return deprecated message of the filter 312 * 313 * @return string 314 */ 315 public function get_is_deprecated_message(): string { 316 return $this->deprecatedmessage; 317 } 318 319 /** 320 * Set the options for the filter in the format that the filter class expected (e.g. the "select" filter expects an array) 321 * 322 * This method should only be used if the options do not require any calculations/queries, in which 323 * case {@see set_options_callback} should be used. For performance, {@see get_string} shouldn't be used either, use of 324 * {@see lang_string} is instead encouraged 325 * 326 * @param mixed $options 327 * @return self 328 */ 329 public function set_options($options): self { 330 $this->options = $options; 331 return $this; 332 } 333 334 /** 335 * Set the options for the filter to be returned by a callback (that receives no arguments) in the format that the filter 336 * class expects 337 * 338 * @param callable $callback 339 * @return self 340 */ 341 public function set_options_callback(callable $callback): self { 342 $this->options = $callback; 343 return $this; 344 } 345 346 /** 347 * Get the options for the filter, returning via the the previously set options or generated via defined options callback 348 * 349 * @return mixed 350 */ 351 public function get_options() { 352 if (is_callable($this->options)) { 353 $callable = $this->options; 354 $this->options = ($callable)(); 355 } 356 return $this->options; 357 } 358 359 /** 360 * Set a limited subset of operators that should be used for the filter, refer to each filter class to find defined 361 * operator constants 362 * 363 * @param array $limitoperators Simple array of operator values 364 * @return self 365 */ 366 public function set_limited_operators(array $limitoperators): self { 367 $this->limitoperators = $limitoperators; 368 return $this; 369 } 370 371 /** 372 * Filter given operators to include only those previously defined by {@see set_limited_operators} 373 * 374 * @param array $operators All operators as defined by the filter class 375 * @return array 376 */ 377 public function restrict_limited_operators(array $operators): array { 378 if (empty($this->limitoperators)) { 379 return $operators; 380 } 381 382 return array_intersect_key($operators, array_flip($this->limitoperators)); 383 } 384 385 /** 386 * Set filter persistent 387 * 388 * @param filter_model $persistent 389 * @return self 390 */ 391 public function set_persistent(filter_model $persistent): self { 392 $this->persistent = $persistent; 393 return $this; 394 } 395 396 /** 397 * Return filter persistent 398 * 399 * @return filter_model|null 400 */ 401 public function get_persistent(): ?filter_model { 402 return $this->persistent ?? null; 403 } 404 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body