MDL-69145 core: Set the default filterset join type to ALL
[moodle.git] / lib / table / classes / local / filter / filter.php
CommitLineData
44effcb4
AN
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
26declare(strict_types=1);
27
28namespace core_table\local\filter;
29
30use Countable;
1592c3c4 31use JsonSerializable;
44effcb4
AN
32use InvalidArgumentException;
33use Iterator;
34
35/**
36 * Class representing a generic filter of any type.
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 */
1592c3c4 42class filter implements Countable, Iterator, JsonSerializable {
44effcb4 43
a489f20f 44 /** @var int The default filter type (ANY) */
9e791ff7 45 const JOINTYPE_DEFAULT = 1;
44effcb4
AN
46
47 /** @var int None of the following match */
48 const JOINTYPE_NONE = 0;
49
50 /** @var int Any of the following match */
51 const JOINTYPE_ANY = 1;
52
53 /** @var int All of the following match */
54 const JOINTYPE_ALL = 2;
55
56 /** @var string The name of this filter */
57 protected $name = null;
58
59 /** @var int The join type currently in use */
60 protected $jointype = self::JOINTYPE_DEFAULT;
61
62 /** @var array The list of active filter values */
63 protected $filtervalues = [];
64
65 /** @var int[] valid join types */
66 protected $jointypes = [
67 self::JOINTYPE_NONE,
68 self::JOINTYPE_ANY,
69 self::JOINTYPE_ALL,
70 ];
71
72 /** @var int The current iterator position */
73 protected $iteratorposition = null;
74
75 /**
76 * Constructor for the generic filter class.
77 *
78 * @param string $name The name of the current filter.
79 * @param int $jointype The join to use when combining the filters.
80 * See the JOINTYPE_ constants for further information on the field.
81 * @param mixed[] $values An array of filter objects to be applied.
82 */
83 public function __construct(string $name, ?int $jointype = null, ?array $values = null) {
84 $this->name = $name;
85
86 if ($jointype !== null) {
87 $this->set_join_type($jointype);
88 }
89
90 if (!empty($values)) {
91 foreach ($values as $value) {
92 $this->add_filter_value($value);
93 }
94 }
95 }
96
97 /**
98 * Reset the iterator position.
99 */
100 public function reset_iterator(): void {
101 $this->iteratorposition = null;
102 }
103
104 /**
105 * Return the current filter value.
106 */
107 public function current() {
108 if ($this->iteratorposition === null) {
109 $this->rewind();
110 }
111
112 if ($this->iteratorposition === null) {
113 return null;
114 }
115
116 return $this->filtervalues[$this->iteratorposition];
117 }
118
119 /**
120 * Returns the current position of the iterator.
121 *
122 * @return int
123 */
124 public function key() {
125 if ($this->iteratorposition === null) {
126 $this->rewind();
127 }
128
129 return $this->iteratorposition;
130 }
131
132 /**
133 * Rewind the Iterator position to the start.
134 */
135 public function rewind(): void {
136 if ($this->iteratorposition === null) {
137 $this->sort_filter_values();
138 }
139
140 if (count($this->filtervalues)) {
141 $this->iteratorposition = 0;
142 }
143 }
144
145 /**
146 * Move to the next value in the list.
147 */
148 public function next(): void {
149 ++$this->iteratorposition;
150 }
151
152 /**
153 * Check if the current position is valid.
154 *
155 * @return bool
156 */
157 public function valid(): bool {
158 return isset($this->filtervalues[$this->iteratorposition]);
159 }
160
161 /**
162 * Return the number of contexts.
163 *
164 * @return int
165 */
166 public function count(): int {
167 return count($this->filtervalues);
168 }
169
170 /**
171 * Return the name of the filter.
172 *
173 * @return string
174 */
175 public function get_name(): string {
176 return $this->name;
177 }
178
179 /**
180 * Specify the type of join to employ for the filter.
181 *
182 * @param int $jointype The join type to use using one of the supplied constants
183 * @return self
184 */
185 public function set_join_type(int $jointype): self {
186 if (array_search($jointype, $this->jointypes) === false) {
187 throw new InvalidArgumentException('Invalid join type specified');
188 }
189
190 $this->jointype = $jointype;
191
192 return $this;
193 }
194
195 /**
196 * Return the currently specified join type.
197 *
198 * @return int
199 */
200 public function get_join_type(): int {
201 return $this->jointype;
202 }
203
204 /**
205 * Add a value to the filter.
206 *
207 * @param mixed $value
208 * @return self
209 */
210 public function add_filter_value($value): self {
211 if ($value === null) {
212 // Null values are usually invalid.
213 return $this;
214 }
215
216 if ($value === '') {
217 // Empty strings are invalid.
218 return $this;
219 }
220
221 if (array_search($value, $this->filtervalues) !== false) {
222 // Remove duplicates.
223 return $this;
224 }
225
226 $this->filtervalues[] = $value;
227
228 // Reset the iterator position.
229 $this->reset_iterator();
230
231 return $this;
232 }
233
234 /**
235 * Sort the filter values to ensure reliable, and consistent output.
236 */
237 protected function sort_filter_values(): void {
238 // Sort the filter values to ensure consistent output.
239 // Note: This is not a locale-aware sort, but we don't need this.
240 // It's primarily for consistency, not for actual sorting.
241 sort($this->filtervalues);
242
243 $this->reset_iterator();
244 }
245
246 /**
247 * Return the current filter values.
248 *
249 * @return mixed[]
250 */
251 public function get_filter_values(): array {
252 $this->sort_filter_values();
253 return $this->filtervalues;
254 }
1592c3c4
SL
255
256 /**
257 * Serialize filter.
258 *
259 * @return mixed|object
260 */
261 public function jsonSerialize() {
262 return (object) [
263 'name' => $this->get_name(),
264 'jointype' => $this->get_join_type(),
265 'values' => $this->get_filter_values(),
266 ];
267 }
44effcb4 268}