/lib/table/classes/local/filter/filter.php

https://github.com/markn86/moodle · PHP · 268 lines · 110 code · 43 blank · 115 comment · 11 complexity · 63e470b67db9990db2d9e583cb4e2d06 MD5 · raw file

  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. * Table filterset.
  18. *
  19. * @package core
  20. * @category table
  21. * @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
  22. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23. */
  24. declare(strict_types=1);
  25. namespace core_table\local\filter;
  26. use Countable;
  27. use JsonSerializable;
  28. use InvalidArgumentException;
  29. use Iterator;
  30. /**
  31. * Class representing a generic filter of any type.
  32. *
  33. * @package core
  34. * @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
  35. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36. */
  37. class filter implements Countable, Iterator, JsonSerializable {
  38. /** @var int The default filter type (ANY) */
  39. const JOINTYPE_DEFAULT = 1;
  40. /** @var int None of the following match */
  41. const JOINTYPE_NONE = 0;
  42. /** @var int Any of the following match */
  43. const JOINTYPE_ANY = 1;
  44. /** @var int All of the following match */
  45. const JOINTYPE_ALL = 2;
  46. /** @var string The name of this filter */
  47. protected $name = null;
  48. /** @var int The join type currently in use */
  49. protected $jointype = self::JOINTYPE_DEFAULT;
  50. /** @var array The list of active filter values */
  51. protected $filtervalues = [];
  52. /** @var int[] valid join types */
  53. protected $jointypes = [
  54. self::JOINTYPE_NONE,
  55. self::JOINTYPE_ANY,
  56. self::JOINTYPE_ALL,
  57. ];
  58. /** @var int The current iterator position */
  59. protected $iteratorposition = null;
  60. /**
  61. * Constructor for the generic filter class.
  62. *
  63. * @param string $name The name of the current filter.
  64. * @param int $jointype The join to use when combining the filters.
  65. * See the JOINTYPE_ constants for further information on the field.
  66. * @param mixed[] $values An array of filter objects to be applied.
  67. */
  68. public function __construct(string $name, ?int $jointype = null, ?array $values = null) {
  69. $this->name = $name;
  70. if ($jointype !== null) {
  71. $this->set_join_type($jointype);
  72. }
  73. if (!empty($values)) {
  74. foreach ($values as $value) {
  75. $this->add_filter_value($value);
  76. }
  77. }
  78. }
  79. /**
  80. * Reset the iterator position.
  81. */
  82. public function reset_iterator(): void {
  83. $this->iteratorposition = null;
  84. }
  85. /**
  86. * Return the current filter value.
  87. */
  88. public function current() {
  89. if ($this->iteratorposition === null) {
  90. $this->rewind();
  91. }
  92. if ($this->iteratorposition === null) {
  93. return null;
  94. }
  95. return $this->filtervalues[$this->iteratorposition];
  96. }
  97. /**
  98. * Returns the current position of the iterator.
  99. *
  100. * @return int
  101. */
  102. public function key() {
  103. if ($this->iteratorposition === null) {
  104. $this->rewind();
  105. }
  106. return $this->iteratorposition;
  107. }
  108. /**
  109. * Rewind the Iterator position to the start.
  110. */
  111. public function rewind(): void {
  112. if ($this->iteratorposition === null) {
  113. $this->sort_filter_values();
  114. }
  115. if (count($this->filtervalues)) {
  116. $this->iteratorposition = 0;
  117. }
  118. }
  119. /**
  120. * Move to the next value in the list.
  121. */
  122. public function next(): void {
  123. ++$this->iteratorposition;
  124. }
  125. /**
  126. * Check if the current position is valid.
  127. *
  128. * @return bool
  129. */
  130. public function valid(): bool {
  131. return isset($this->filtervalues[$this->iteratorposition]);
  132. }
  133. /**
  134. * Return the number of contexts.
  135. *
  136. * @return int
  137. */
  138. public function count(): int {
  139. return count($this->filtervalues);
  140. }
  141. /**
  142. * Return the name of the filter.
  143. *
  144. * @return string
  145. */
  146. public function get_name(): string {
  147. return $this->name;
  148. }
  149. /**
  150. * Specify the type of join to employ for the filter.
  151. *
  152. * @param int $jointype The join type to use using one of the supplied constants
  153. * @return self
  154. */
  155. public function set_join_type(int $jointype): self {
  156. if (array_search($jointype, $this->jointypes) === false) {
  157. throw new InvalidArgumentException('Invalid join type specified');
  158. }
  159. $this->jointype = $jointype;
  160. return $this;
  161. }
  162. /**
  163. * Return the currently specified join type.
  164. *
  165. * @return int
  166. */
  167. public function get_join_type(): int {
  168. return $this->jointype;
  169. }
  170. /**
  171. * Add a value to the filter.
  172. *
  173. * @param mixed $value
  174. * @return self
  175. */
  176. public function add_filter_value($value): self {
  177. if ($value === null) {
  178. // Null values are usually invalid.
  179. return $this;
  180. }
  181. if ($value === '') {
  182. // Empty strings are invalid.
  183. return $this;
  184. }
  185. if (array_search($value, $this->filtervalues) !== false) {
  186. // Remove duplicates.
  187. return $this;
  188. }
  189. $this->filtervalues[] = $value;
  190. // Reset the iterator position.
  191. $this->reset_iterator();
  192. return $this;
  193. }
  194. /**
  195. * Sort the filter values to ensure reliable, and consistent output.
  196. */
  197. protected function sort_filter_values(): void {
  198. // Sort the filter values to ensure consistent output.
  199. // Note: This is not a locale-aware sort, but we don't need this.
  200. // It's primarily for consistency, not for actual sorting.
  201. sort($this->filtervalues);
  202. $this->reset_iterator();
  203. }
  204. /**
  205. * Return the current filter values.
  206. *
  207. * @return mixed[]
  208. */
  209. public function get_filter_values(): array {
  210. $this->sort_filter_values();
  211. return $this->filtervalues;
  212. }
  213. /**
  214. * Serialize filter.
  215. *
  216. * @return mixed|object
  217. */
  218. public function jsonSerialize() {
  219. return (object) [
  220. 'name' => $this->get_name(),
  221. 'jointype' => $this->get_join_type(),
  222. 'values' => $this->get_filter_values(),
  223. ];
  224. }
  225. }