| 1 | efrain | 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 MoodleQuickForm;
 | 
        
           |  |  | 22 | use core_reportbuilder\local\helpers\database;
 | 
        
           |  |  | 23 |   | 
        
           |  |  | 24 | /**
 | 
        
           |  |  | 25 |  * Select report filter
 | 
        
           |  |  | 26 |  *
 | 
        
           |  |  | 27 |  * The options for the select are defined when creating the filter by calling {@see set_options} or {@see set_options_callback}
 | 
        
           |  |  | 28 |  *
 | 
        
           |  |  | 29 |  * To extend this class in your own filter (e.g. to pre-populate available options), you should override the {@see get_operators}
 | 
        
           |  |  | 30 |  * and/or {@see get_select_options} methods
 | 
        
           |  |  | 31 |  *
 | 
        
           |  |  | 32 |  * @package     core_reportbuilder
 | 
        
           |  |  | 33 |  * @copyright   2021 David Matamoros <davidmc@moodle.com>
 | 
        
           |  |  | 34 |  * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 35 |  */
 | 
        
           |  |  | 36 | class select extends base {
 | 
        
           |  |  | 37 |   | 
        
           |  |  | 38 |     /** @var int Any value */
 | 
        
           |  |  | 39 |     public const ANY_VALUE = 0;
 | 
        
           |  |  | 40 |   | 
        
           |  |  | 41 |     /** @var int Equal to */
 | 
        
           |  |  | 42 |     public const EQUAL_TO = 1;
 | 
        
           |  |  | 43 |   | 
        
           |  |  | 44 |     /** @var int Not equal to */
 | 
        
           |  |  | 45 |     public const NOT_EQUAL_TO = 2;
 | 
        
           |  |  | 46 |   | 
        
           | 1441 | ariadna | 47 |     /** @var int Value to indicate "Any value" for the simplified filter options  */
 | 
        
           |  |  | 48 |     private const OPTION_ANY_VALUE = -124567;
 | 
        
           |  |  | 49 |   | 
        
           | 1 | efrain | 50 |     /**
 | 
        
           |  |  | 51 |      * Returns an array of comparison operators
 | 
        
           |  |  | 52 |      *
 | 
        
           |  |  | 53 |      * @return array
 | 
        
           |  |  | 54 |      */
 | 
        
           |  |  | 55 |     protected function get_operators(): array {
 | 
        
           |  |  | 56 |         $operators = [
 | 
        
           |  |  | 57 |             self::ANY_VALUE => get_string('filterisanyvalue', 'core_reportbuilder'),
 | 
        
           |  |  | 58 |             self::EQUAL_TO => get_string('filterisequalto', 'core_reportbuilder'),
 | 
        
           |  |  | 59 |             self::NOT_EQUAL_TO => get_string('filterisnotequalto', 'core_reportbuilder')
 | 
        
           |  |  | 60 |         ];
 | 
        
           |  |  | 61 |   | 
        
           |  |  | 62 |         return $this->filter->restrict_limited_operators($operators);
 | 
        
           |  |  | 63 |     }
 | 
        
           |  |  | 64 |   | 
        
           |  |  | 65 |     /**
 | 
        
           |  |  | 66 |      * Return the options for the filter as an array, to be used to populate the select input field
 | 
        
           |  |  | 67 |      *
 | 
        
           |  |  | 68 |      * @return array
 | 
        
           |  |  | 69 |      */
 | 
        
           |  |  | 70 |     protected function get_select_options(): array {
 | 
        
           | 1441 | ariadna | 71 |         static $options = [];
 | 
        
           |  |  | 72 |   | 
        
           |  |  | 73 |         if (!array_key_exists($this->name, $options)) {
 | 
        
           |  |  | 74 |             $options[$this->name] = (array) $this->filter->get_options();
 | 
        
           |  |  | 75 |         }
 | 
        
           |  |  | 76 |   | 
        
           |  |  | 77 |         return $options[$this->name];
 | 
        
           | 1 | efrain | 78 |     }
 | 
        
           |  |  | 79 |   | 
        
           |  |  | 80 |     /**
 | 
        
           |  |  | 81 |      * Adds controls specific to this filter in the form.
 | 
        
           |  |  | 82 |      *
 | 
        
           |  |  | 83 |      * @param MoodleQuickForm $mform
 | 
        
           |  |  | 84 |      */
 | 
        
           |  |  | 85 |     public function setup_form(MoodleQuickForm $mform): void {
 | 
        
           | 1441 | ariadna | 86 |         $operators = $this->get_operators();
 | 
        
           | 1 | efrain | 87 |         $options = $this->get_select_options();
 | 
        
           |  |  | 88 |   | 
        
           | 1441 | ariadna | 89 |         // If a multidimensional array is passed, we need to use a different element type.
 | 
        
           |  |  | 90 |         $optioncountrecursive = count($options, COUNT_RECURSIVE);
 | 
        
           |  |  | 91 |         $element = (count($options) === $optioncountrecursive ? 'select' : 'selectgroups');
 | 
        
           | 1 | efrain | 92 |   | 
        
           | 1441 | ariadna | 93 |         // If operators are unrestricted, and we have upto two options, then simplify the filter to list only those.
 | 
        
           |  |  | 94 |         if (count($operators) === 3 && $optioncountrecursive <= 2) {
 | 
        
           |  |  | 95 |             $mform->addElement('hidden', "{$this->name}_operator");
 | 
        
           |  |  | 96 |             $mform->setType("{$this->name}_operator", PARAM_INT);
 | 
        
           |  |  | 97 |             $mform->setConstant("{$this->name}_operator", self::EQUAL_TO);
 | 
        
           |  |  | 98 |   | 
        
           |  |  | 99 |             $mform->addElement(
 | 
        
           |  |  | 100 |                 $element,
 | 
        
           |  |  | 101 |                 "{$this->name}_value",
 | 
        
           |  |  | 102 |                 get_string('filterfieldvalue', 'core_reportbuilder', $this->get_header()),
 | 
        
           |  |  | 103 |                 [self::OPTION_ANY_VALUE => $operators[self::ANY_VALUE]] + $options,
 | 
        
           |  |  | 104 |             )->setHiddenLabel(true);
 | 
        
           |  |  | 105 |         } else {
 | 
        
           |  |  | 106 |             $elements = [];
 | 
        
           |  |  | 107 |   | 
        
           |  |  | 108 |             $elements[] = $mform->createElement(
 | 
        
           |  |  | 109 |                 'select',
 | 
        
           |  |  | 110 |                 "{$this->name}_operator",
 | 
        
           |  |  | 111 |                 get_string('filterfieldoperator', 'core_reportbuilder', $this->get_header()),
 | 
        
           |  |  | 112 |                 $operators,
 | 
        
           |  |  | 113 |             );
 | 
        
           |  |  | 114 |   | 
        
           |  |  | 115 |             $elements[] = $mform->createElement(
 | 
        
           |  |  | 116 |                 $element,
 | 
        
           |  |  | 117 |                 "{$this->name}_value",
 | 
        
           |  |  | 118 |                 get_string('filterfieldvalue', 'core_reportbuilder', $this->get_header()),
 | 
        
           |  |  | 119 |                 $options,
 | 
        
           |  |  | 120 |             );
 | 
        
           |  |  | 121 |   | 
        
           |  |  | 122 |             $mform->addGroup($elements, "{$this->name}_group", $this->get_header(), '', false)
 | 
        
           |  |  | 123 |                 ->setHiddenLabel(true);
 | 
        
           |  |  | 124 |   | 
        
           |  |  | 125 |             $mform->hideIf("{$this->name}_value", "{$this->name}_operator", 'eq', self::ANY_VALUE);
 | 
        
           |  |  | 126 |         }
 | 
        
           | 1 | efrain | 127 |     }
 | 
        
           |  |  | 128 |   | 
        
           |  |  | 129 |     /**
 | 
        
           |  |  | 130 |      * Return filter SQL
 | 
        
           |  |  | 131 |      *
 | 
        
           |  |  | 132 |      * Note that operators must be of type integer, while values can be integer or string.
 | 
        
           |  |  | 133 |      *
 | 
        
           |  |  | 134 |      * @param array $values
 | 
        
           |  |  | 135 |      * @return array array of two elements - SQL query and named parameters
 | 
        
           |  |  | 136 |      */
 | 
        
           |  |  | 137 |     public function get_sql_filter(array $values): array {
 | 
        
           |  |  | 138 |         $name = database::generate_param_name();
 | 
        
           |  |  | 139 |   | 
        
           | 1441 | ariadna | 140 |         $operator = (int) ($values["{$this->name}_operator"] ?? self::ANY_VALUE);
 | 
        
           |  |  | 141 |         $value = (string) ($values["{$this->name}_value"] ?? self::OPTION_ANY_VALUE);
 | 
        
           | 1 | efrain | 142 |   | 
        
           |  |  | 143 |         $fieldsql = $this->filter->get_field_sql();
 | 
        
           |  |  | 144 |         $params = $this->filter->get_field_params();
 | 
        
           |  |  | 145 |   | 
        
           | 1441 | ariadna | 146 |         // Get available options, if multidimensional then flatten the array.
 | 
        
           |  |  | 147 |         $options = $this->get_select_options();
 | 
        
           |  |  | 148 |         if (count($options) !== count($options, COUNT_RECURSIVE)) {
 | 
        
           |  |  | 149 |             $options = array_merge(...array_values($options));
 | 
        
           |  |  | 150 |         }
 | 
        
           |  |  | 151 |   | 
        
           | 1 | efrain | 152 |         // Validate filter form values.
 | 
        
           | 1441 | ariadna | 153 |         if ($operator === self::ANY_VALUE || !array_key_exists($value, $options)) {
 | 
        
           | 1 | efrain | 154 |             return ['', []];
 | 
        
           |  |  | 155 |         }
 | 
        
           |  |  | 156 |   | 
        
           |  |  | 157 |         switch ($operator) {
 | 
        
           |  |  | 158 |             case self::EQUAL_TO:
 | 
        
           |  |  | 159 |                 $fieldsql .= "=:$name";
 | 
        
           |  |  | 160 |                 $params[$name] = $value;
 | 
        
           |  |  | 161 |                 break;
 | 
        
           |  |  | 162 |             case self::NOT_EQUAL_TO:
 | 
        
           |  |  | 163 |                 $fieldsql .= "<>:$name";
 | 
        
           |  |  | 164 |                 $params[$name] = $value;
 | 
        
           |  |  | 165 |                 break;
 | 
        
           |  |  | 166 |             default:
 | 
        
           |  |  | 167 |                 return ['', []];
 | 
        
           |  |  | 168 |         }
 | 
        
           |  |  | 169 |         return [$fieldsql, $params];
 | 
        
           |  |  | 170 |     }
 | 
        
           |  |  | 171 |   | 
        
           |  |  | 172 |     /**
 | 
        
           |  |  | 173 |      * Return sample filter values
 | 
        
           |  |  | 174 |      *
 | 
        
           |  |  | 175 |      * @return array
 | 
        
           |  |  | 176 |      */
 | 
        
           |  |  | 177 |     public function get_sample_values(): array {
 | 
        
           |  |  | 178 |         return [
 | 
        
           |  |  | 179 |             "{$this->name}_operator" => self::EQUAL_TO,
 | 
        
           |  |  | 180 |             "{$this->name}_value" => 1,
 | 
        
           |  |  | 181 |         ];
 | 
        
           |  |  | 182 |     }
 | 
        
           |  |  | 183 | }
 |