See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]
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 int The default filter type (ALL) */ 44 const JOINTYPE_DEFAULT = 2; 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 = null; 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 if ($this->jointype === null) { 94 $this->jointype = self::JOINTYPE_DEFAULT; 95 } 96 return $this->jointype; 97 } 98 99 /** 100 * Add the specified filter. 101 * 102 * @param filter $filter 103 * @return self 104 */ 105 public function add_filter(filter $filter): self { 106 $filtername = $filter->get_name(); 107 108 if (array_key_exists($filtername, $this->filters)) { 109 // This filter already exists. 110 if ($this->filters[$filtername] === $filter) { 111 // This is the same value as already added. 112 // Just ignore it. 113 return $this; 114 } 115 116 // This is a different value to last time. Fail as this is not supported. 117 throw new UnexpectedValueException( 118 "A filter of type '{$filtername}' has already been added. Check that you have the correct filter." 119 ); 120 } 121 122 // Ensure that the filter is both known, and is of the correct type. 123 $validtypes = $this->get_all_filtertypes(); 124 125 if (!array_key_exists($filtername, $validtypes)) { 126 // Unknown filter. 127 throw new InvalidArgumentException( 128 "The filter '{$filtername}' was not recognised." 129 ); 130 } 131 132 // Check that the filter is of the correct type. 133 if (!is_a($filter, $validtypes[$filtername])) { 134 $actualtype = get_class($filter); 135 $requiredtype = $validtypes[$filtername]; 136 137 throw new InvalidArgumentException( 138 "The filter '{$filtername}' was incorrectly specified as a {$actualtype}. It must be a {$requiredtype}." 139 ); 140 } 141 142 // All good. Add the filter. 143 $this->filters[$filtername] = $filter; 144 145 return $this; 146 } 147 148 /** 149 * Add the specified filter from the supplied params. 150 * 151 * @param string $filtername The name of the filter to create 152 * @param mixed[] ...$args Additional arguments used to create this filter type 153 * @return self 154 */ 155 public function add_filter_from_params(string $filtername, ...$args): self { 156 // Fetch the list of valid filters by name. 157 $validtypes = $this->get_all_filtertypes(); 158 159 if (!array_key_exists($filtername, $validtypes)) { 160 // Unknown filter. 161 throw new InvalidArgumentException( 162 "The filter '{$filtername}' was not recognised." 163 ); 164 } 165 166 $filterclass = $validtypes[$filtername]; 167 168 if (!class_exists($filterclass)) { 169 // Filter class cannot be class autoloaded. 170 throw new InvalidArgumentException( 171 "The filter class '{$filterclass}' for filter '{$filtername}' could not be found." 172 ); 173 } 174 175 // Pass all supplied arguments to the constructor when adding a new filter. 176 // This allows for a wider definition of the the filter in child classes. 177 $this->add_filter(new $filterclass($filtername, ...$args)); 178 179 return $this; 180 } 181 182 /** 183 * Return the current set of filters. 184 * 185 * @return filter[] 186 */ 187 public function get_filters(): array { 188 // Sort the filters by their name to ensure consistent output. 189 // Note: This is not a locale-aware sort, but we don't need this. 190 // It's primarily for consistency, not for actual sorting. 191 asort($this->filters); 192 193 return $this->filters; 194 } 195 196 /** 197 * Check whether the filter has been added or not. 198 * 199 * @param string $filtername 200 * @return bool 201 */ 202 public function has_filter(string $filtername): bool { 203 // We do not check if the filtername is valid, only that it exists. 204 // This is an existence check and there is no benefit to doing any more. 205 return array_key_exists($filtername, $this->filters); 206 } 207 208 /** 209 * Get the named filter. 210 * 211 * @param string $filtername 212 * @return filter 213 */ 214 public function get_filter(string $filtername): filter { 215 if (!array_key_exists($filtername, $this->get_all_filtertypes())) { 216 throw new UnexpectedValueException("The filter specified ({$filtername}) is invalid."); 217 } 218 219 if (!array_key_exists($filtername, $this->filters)) { 220 throw new UnexpectedValueException("The filter specified ({$filtername}) has not been created."); 221 } 222 223 return $this->filters[$filtername]; 224 } 225 226 /** 227 * Confirm whether the filter has been correctly specified. 228 * 229 * @throws moodle_exception 230 */ 231 public function check_validity(): void { 232 // Ensure that all required filters are present. 233 $missing = []; 234 foreach (array_keys($this->get_required_filters()) as $filtername) { 235 if (!array_key_exists($filtername, $this->filters)) { 236 $missing[] = $filtername; 237 } 238 } 239 240 if (!empty($missing)) { 241 throw new moodle_exception( 242 'missingrequiredfields', 243 'core_table', 244 '', 245 implode(get_string('listsep', 'langconfig') . ' ', $missing) 246 ); 247 } 248 } 249 250 /** 251 * Get the list of required filters in an array of filtername => filter class type. 252 * 253 * @return array 254 */ 255 protected function get_required_filters(): array { 256 return []; 257 } 258 259 /** 260 * Get the list of optional filters in an array of filtername => filter class type. 261 * 262 * @return array 263 */ 264 protected function get_optional_filters(): array { 265 return []; 266 } 267 268 /** 269 * Get all filter valid types in an array of filtername => filter class type. 270 * 271 * @return array 272 */ 273 public function get_all_filtertypes(): array { 274 if ($this->filtertypes === null) { 275 $required = $this->get_required_filters(); 276 $optional = $this->get_optional_filters(); 277 278 $conflicts = array_keys(array_intersect_key($required, $optional)); 279 280 if (!empty($conflicts)) { 281 throw new InvalidArgumentException( 282 "Some filter types are both required, and optional: " . implode(', ', $conflicts) 283 ); 284 } 285 286 $this->filtertypes = array_merge($required, $optional); 287 asort($this->filtertypes); 288 } 289 290 return $this->filtertypes; 291 } 292 293 /** 294 * Serialize filterset. 295 * 296 * @return mixed|object 297 */ 298 #[\ReturnTypeWillChange] 299 public function jsonSerialize() { 300 return (object) [ 301 'jointype' => $this->get_join_type(), 302 'filters' => $this->get_filters(), 303 ]; 304 } 305 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body