See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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 /** 18 * Table filterset. 19 * 20 * @package core 21 * @category table 22 * @copyright 2020 Andrew Nicols <andrew@nicols.co.uk> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 declare(strict_types=1); 27 28 namespace core_table\local\filter; 29 30 use InvalidArgumentException; 31 use JsonSerializable; 32 use UnexpectedValueException; 33 use moodle_exception; 34 35 /** 36 * Class representing a set of filters. 37 * 38 * @package core 39 * @copyright 2020 Andrew Nicols <andrew@nicols.co.uk> 40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 41 */ 42 abstract class filterset implements JsonSerializable { 43 /** @var in The default filter type (ANY) */ 44 const JOINTYPE_DEFAULT = 1; 45 46 /** @var int None of the following match */ 47 const JOINTYPE_NONE = 0; 48 49 /** @var int Any of the following match */ 50 const JOINTYPE_ANY = 1; 51 52 /** @var int All of the following match */ 53 const JOINTYPE_ALL = 2; 54 55 /** @var int The join type currently in use */ 56 protected $jointype = self::JOINTYPE_DEFAULT; 57 58 /** @var array The list of combined filter types */ 59 protected $filtertypes = null; 60 61 /** @var array The list of active filters */ 62 protected $filters = []; 63 64 /** @var int[] valid join types */ 65 protected $jointypes = [ 66 self::JOINTYPE_NONE, 67 self::JOINTYPE_ANY, 68 self::JOINTYPE_ALL, 69 ]; 70 71 /** 72 * Specify the type of join to employ for the filter. 73 * 74 * @param int $jointype The join type to use using one of the supplied constants 75 * @return self 76 */ 77 public function set_join_type(int $jointype): self { 78 if (array_search($jointype, $this->jointypes) === false) { 79 throw new InvalidArgumentException('Invalid join type specified'); 80 } 81 82 $this->jointype = $jointype; 83 84 return $this; 85 } 86 87 /** 88 * Return the currently specified join type. 89 * 90 * @return int 91 */ 92 public function get_join_type(): int { 93 return $this->jointype; 94 } 95 96 /** 97 * Add the specified filter. 98 * 99 * @param filter $filter 100 * @return self 101 */ 102 public function add_filter(filter $filter): self { 103 $filtername = $filter->get_name(); 104 105 if (array_key_exists($filtername, $this->filters)) { 106 // This filter already exists. 107 if ($this->filters[$filtername] === $filter) { 108 // This is the same value as already added. 109 // Just ignore it. 110 return $this; 111 } 112 113 // This is a different value to last time. Fail as this is not supported. 114 throw new UnexpectedValueException( 115 "A filter of type '{$filtername}' has already been added. Check that you have the correct filter." 116 ); 117 } 118 119 // Ensure that the filter is both known, and is of the correct type. 120 $validtypes = $this->get_all_filtertypes(); 121 122 if (!array_key_exists($filtername, $validtypes)) { 123 // Unknown filter. 124 throw new InvalidArgumentException( 125 "The filter '{$filtername}' was not recognised." 126 ); 127 } 128 129 // Check that the filter is of the correct type. 130 if (!is_a($filter, $validtypes[$filtername])) { 131 $actualtype = get_class($filter); 132 $requiredtype = $validtypes[$filtername]; 133 134 throw new InvalidArgumentException( 135 "The filter '{$filtername}' was incorrectly specified as a {$actualtype}. It must be a {$requiredtype}." 136 ); 137 } 138 139 // All good. Add the filter. 140 $this->filters[$filtername] = $filter; 141 142 return $this; 143 } 144 145 /** 146 * Add the specified filter from the supplied params. 147 * 148 * @param string $filtername The name of the filter to create 149 * @param mixed[] ...$args Additional arguments used to create this filter type 150 * @return self 151 */ 152 public function add_filter_from_params(string $filtername, ...$args): self { 153 // Fetch the list of valid filters by name. 154 $validtypes = $this->get_all_filtertypes(); 155 156 if (!array_key_exists($filtername, $validtypes)) { 157 // Unknown filter. 158 throw new InvalidArgumentException( 159 "The filter '{$filtername}' was not recognised." 160 ); 161 } 162 163 $filterclass = $validtypes[$filtername]; 164 165 if (!class_exists($filterclass)) { 166 // Filter class cannot be class autoloaded. 167 throw new InvalidArgumentException( 168 "The filter class '{$filterclass}' for filter '{$filtername}' could not be found." 169 ); 170 } 171 172 // Pass all supplied arguments to the constructor when adding a new filter. 173 // This allows for a wider definition of the the filter in child classes. 174 $this->add_filter(new $filterclass($filtername, ...$args)); 175 176 return $this; 177 } 178 179 /** 180 * Return the current set of filters. 181 * 182 * @return filter[] 183 */ 184 public function get_filters(): array { 185 // Sort the filters by their name to ensure consistent output. 186 // Note: This is not a locale-aware sort, but we don't need this. 187 // It's primarily for consistency, not for actual sorting. 188 asort($this->filters); 189 190 return $this->filters; 191 } 192 193 /** 194 * Check whether the filter has been added or not. 195 * 196 * @param string $filtername 197 * @return bool 198 */ 199 public function has_filter(string $filtername): bool { 200 // We do not check if the filtername is valid, only that it exists. 201 // This is an existence check and there is no benefit to doing any more. 202 return array_key_exists($filtername, $this->filters); 203 } 204 205 /** 206 * Get the named filter. 207 * 208 * @param string $filtername 209 * @return filter 210 */ 211 public function get_filter(string $filtername): filter { 212 if (!array_key_exists($filtername, $this->get_all_filtertypes())) { 213 throw new UnexpectedValueException("The filter specified ({$filtername}) is invalid."); 214 } 215 216 if (!array_key_exists($filtername, $this->filters)) { 217 throw new UnexpectedValueException("The filter specified ({$filtername}) has not been created."); 218 } 219 220 return $this->filters[$filtername]; 221 } 222 223 /** 224 * Confirm whether the filter has been correctly specified. 225 * 226 * @throws moodle_exception 227 */ 228 public function check_validity(): void { 229 // Ensure that all required filters are present. 230 $missing = []; 231 foreach (array_keys($this->get_required_filters()) as $filtername) { 232 if (!array_key_exists($filtername, $this->filters)) { 233 $missing[] = $filtername; 234 } 235 } 236 237 if (!empty($missing)) { 238 throw new moodle_exception( 239 'missingrequiredfields', 240 'core_table', 241 '', 242 implode(get_string('listsep', 'langconfig') . ' ', $missing) 243 ); 244 } 245 } 246 247 /** 248 * Get the list of required filters in an array of filtername => filter class type. 249 * 250 * @return array 251 */ 252 protected function get_required_filters(): array { 253 return []; 254 } 255 256 /** 257 * Get the list of optional filters in an array of filtername => filter class type. 258 * 259 * @return array 260 */ 261 protected function get_optional_filters(): array { 262 return []; 263 } 264 265 /** 266 * Get all filter valid types in an array of filtername => filter class type. 267 * 268 * @return array 269 */ 270 public function get_all_filtertypes(): array { 271 if ($this->filtertypes === null) { 272 $required = $this->get_required_filters(); 273 $optional = $this->get_optional_filters(); 274 275 $conflicts = array_keys(array_intersect_key($required, $optional)); 276 277 if (!empty($conflicts)) { 278 throw new InvalidArgumentException( 279 "Some filter types are both required, and optional: " . implode(', ', $conflicts) 280 ); 281 } 282 283 $this->filtertypes = array_merge($required, $optional); 284 asort($this->filtertypes); 285 } 286 287 return $this->filtertypes; 288 } 289 290 /** 291 * Serialize filterset. 292 * 293 * @return mixed|object 294 */ 295 public function jsonSerialize() { 296 return (object) [ 297 'jointype' => $this->get_join_type(), 298 'filters' => $this->get_filters(), 299 ]; 300 } 301 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body