Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   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  declare(strict_types=1);
  18  
  19  namespace core_reportbuilder\local\filters;
  20  
  21  use coding_exception;
  22  use core_tag_tag;
  23  use lang_string;
  24  use MoodleQuickForm;
  25  use stdClass;
  26  use core_reportbuilder\local\helpers\database;
  27  
  28  /**
  29   * Class containing logic for the tags filter
  30   *
  31   * The field SQL should be the field containing the ID of the {tag} table
  32   *
  33   * @package     core_reportbuilder
  34   * @copyright   2022 Paul Holden <paulh@moodle.com>
  35   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   */
  37  class tags extends base {
  38  
  39      /** @var int Any value */
  40      public const ANY_VALUE = 0;
  41  
  42      /** @var int Tags are present */
  43      public const NOT_EMPTY = 1;
  44  
  45      /** @var int Filter for selected tags */
  46      public const EQUAL_TO = 2;
  47  
  48      /** @var int Tags are not present */
  49      public const EMPTY = 3;
  50  
  51      /** @var int Filter for excluded tags */
  52      public const NOT_EQUAL_TO = 4;
  53  
  54      /**
  55       * Returns an array of comparison operators
  56       *
  57       * @return array
  58       */
  59      private function get_operators(): array {
  60          $operators = [
  61              self::ANY_VALUE => new lang_string('filterisanyvalue', 'core_reportbuilder'),
  62              self::NOT_EMPTY => new lang_string('filterisnotempty', 'core_reportbuilder'),
  63              self::EMPTY => new lang_string('filterisempty', 'core_reportbuilder'),
  64              self::EQUAL_TO => new lang_string('filterisequalto', 'core_reportbuilder'),
  65              self::NOT_EQUAL_TO => new lang_string('filterisnotequalto', 'core_reportbuilder'),
  66          ];
  67  
  68          return $this->filter->restrict_limited_operators($operators);
  69      }
  70  
  71      /**
  72       * Setup form
  73       *
  74       * @param MoodleQuickForm $mform
  75       */
  76      public function setup_form(MoodleQuickForm $mform): void {
  77          global $DB;
  78  
  79          $operatorlabel = get_string('filterfieldoperator', 'core_reportbuilder', $this->get_header());
  80          $mform->addElement('select', "{$this->name}_operator", $operatorlabel, $this->get_operators())
  81              ->setHiddenLabel(true);
  82  
  83          $sql = 'SELECT DISTINCT t.id, t.name, t.rawname
  84                    FROM {tag} t
  85                ORDER BY t.name';
  86  
  87          // Transform tag records into appropriate display name, for selection in the autocomplete element.
  88          $tags = array_map(static function(stdClass $record): string {
  89              return core_tag_tag::make_display_name($record);
  90          }, $DB->get_records_sql($sql));
  91  
  92          $valuelabel = get_string('filterfieldvalue', 'core_reportbuilder', $this->get_header());
  93          $mform->addElement('autocomplete', "{$this->name}_value", $valuelabel, $tags, ['multiple' => true])
  94              ->setHiddenLabel(true);
  95          $mform->hideIf("{$this->name}_value", "{$this->name}_operator", 'in', [self::ANY_VALUE, self::EMPTY, self::NOT_EMPTY]);
  96      }
  97  
  98      /**
  99       * Return filter SQL
 100       *
 101       * @param array $values
 102       * @return array
 103       */
 104      public function get_sql_filter(array $values): array {
 105          global $DB;
 106  
 107          $fieldsql = $this->filter->get_field_sql();
 108          $params = $this->filter->get_field_params();
 109  
 110          $operator = (int) ($values["{$this->name}_operator"] ?? self::ANY_VALUE);
 111          $tags = (array) ($values["{$this->name}_value"] ?? []);
 112  
 113          if ($operator === self::NOT_EMPTY) {
 114              $select = "{$fieldsql} IS NOT NULL";
 115          } else if ($operator === self::EMPTY) {
 116              $select = "{$fieldsql} IS NULL";
 117          } else if ($operator === self::EQUAL_TO && !empty($tags)) {
 118              [$tagselect, $tagselectparams] = $DB->get_in_or_equal($tags, SQL_PARAMS_NAMED,
 119                  database::generate_param_name() . '_');
 120  
 121              $select = "{$fieldsql} {$tagselect}";
 122              $params = array_merge($params, $tagselectparams);
 123          } else if ($operator === self::NOT_EQUAL_TO && !empty($tags)) {
 124              [$tagselect, $tagselectparams] = $DB->get_in_or_equal($tags, SQL_PARAMS_NAMED,
 125                  database::generate_param_name() . '_', false);
 126  
 127              // We should also return those elements that aren't tagged at all.
 128              $select = "COALESCE({$fieldsql}, 0) {$tagselect}";
 129              $params = array_merge($params, $tagselectparams);
 130          } else {
 131              // Invalid/inactive (any value) filter..
 132              return ['', []];
 133          }
 134  
 135          return [$select, $params];
 136      }
 137  
 138      /**
 139       * Return sample filter values
 140       *
 141       * @return array
 142       */
 143      public function get_sample_values(): array {
 144          return [
 145              "{$this->name}_operator" => self::EQUAL_TO,
 146              "{$this->name}_value" => [1],
 147          ];
 148      }
 149  }