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