Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 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 Countable;
  31  use JsonSerializable;
  32  use InvalidArgumentException;
  33  use 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   */
  42  class filter implements Countable, Iterator, JsonSerializable {
  43  
  44      /** @var in The default filter type (ANY) */
  45      const JOINTYPE_DEFAULT = 1;
  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      }
 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      }
 268  }