Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
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\report;
20
 
21
use lang_string;
22
use moodle_exception;
23
use core_reportbuilder\local\filters\base;
1441 ariadna 24
use core_reportbuilder\local\helpers\{database, join_trait};
1 efrain 25
use core_reportbuilder\local\models\filter as filter_model;
26
 
27
/**
28
 * Class to represent a report filter
29
 *
30
 * @package     core_reportbuilder
31
 * @copyright   2021 Paul Holden <paulh@moodle.com>
32
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
33
 */
34
final class filter {
35
 
1441 ariadna 36
    use join_trait;
1 efrain 37
 
38
    /** @var string $fieldsql */
39
    private $fieldsql = '';
40
 
41
    /** @var array $fieldparams */
42
    private $fieldparams = [];
43
 
44
    /** @var bool $available */
1441 ariadna 45
    private $available = true;
1 efrain 46
 
47
    /** @var bool $deprecated */
1441 ariadna 48
    private $deprecated = false;
1 efrain 49
 
50
    /** @var string $deprecatedmessage */
1441 ariadna 51
    private $deprecatedmessage;
1 efrain 52
 
53
    /** @var mixed $options */
1441 ariadna 54
    private $options;
1 efrain 55
 
56
    /** @var array $limitoperators */
1441 ariadna 57
    private $limitoperators = [];
1 efrain 58
 
59
    /** @var filter_model $persistent */
1441 ariadna 60
    private $persistent;
1 efrain 61
 
62
    /**
63
     * Filter constructor
64
     *
65
     * @param string $filterclass Filter type class to use, must extend {@see base} filter class
66
     * @param string $name Internal name of the filter
67
     * @param lang_string $header Title of the filter used in reports
68
     * @param string $entityname Name of the entity this filter belongs to. Typically when creating filters within entities
69
     *      this value should be the result of calling {@see get_entity_name}, however if creating filters inside reports directly
70
     *      it should be the name of the entity as passed to {@see \core_reportbuilder\local\report\base::annotate_entity}
71
     * @param string $fieldsql SQL clause to use for filtering, {@see set_field_sql}
72
     * @param array $fieldparams
73
     * @throws moodle_exception For invalid filter class
74
     */
75
    public function __construct(
1441 ariadna 76
        /** @var string Filter type class to use, must extend {@see base} filter class */
77
        private readonly string $filterclass,
78
        /** @var string Internal name of the filter */
79
        private readonly string $name,
80
        /** @var lang_string Title of the filter used in reports */
81
        private lang_string $header,
82
        /** @var string Name of the entity this filter belongs to */
83
        private readonly string $entityname,
1 efrain 84
        string $fieldsql = '',
1441 ariadna 85
        array $fieldparams = [],
1 efrain 86
    ) {
87
        if (!class_exists($filterclass) || !is_subclass_of($filterclass, base::class)) {
88
            throw new moodle_exception('filterinvalid', 'reportbuilder', '', null, $filterclass);
89
        }
90
 
91
        if ($fieldsql !== '') {
92
            $this->set_field_sql($fieldsql, $fieldparams);
93
        }
94
    }
95
 
96
    /**
97
     * Get filter class path
98
     *
99
     * @return string
100
     */
101
    public function get_filter_class(): string {
102
        return $this->filterclass;
103
    }
104
 
105
    /**
106
     * Get filter name
107
     *
108
     * @return string
109
     */
110
    public function get_name(): string {
111
        return $this->name;
112
    }
113
 
114
    /**
115
     * Return header
116
     *
117
     * @return string
118
     */
119
    public function get_header(): string {
120
        return $this->header->out();
121
    }
122
 
123
    /**
124
     * Set header
125
     *
126
     * @param lang_string $header
127
     * @return self
128
     */
129
    public function set_header(lang_string $header): self {
130
        $this->header = $header;
131
        return $this;
132
    }
133
 
134
    /**
135
     * Return filter entity name
136
     *
137
     * @return string
138
     */
139
    public function get_entity_name(): string {
140
        return $this->entityname;
141
    }
142
 
143
    /**
144
     * Return unique identifier for this filter
145
     *
146
     * @return string
147
     */
148
    public function get_unique_identifier(): string {
149
        return $this->get_entity_name() . ':' . $this->get_name();
150
    }
151
 
152
    /**
153
     * Get SQL expression for the field
154
     *
155
     * @return string
156
     */
157
    public function get_field_sql(): string {
158
        return $this->fieldsql;
159
    }
160
 
161
    /**
162
     * Get the SQL params for the field being filtered
163
     *
164
     * @return array
165
     */
166
    public function get_field_params(): array {
167
        return $this->fieldparams;
168
    }
169
 
170
    /**
171
     * Retrieve SQL expression and parameters for the field
172
     *
173
     * @param int $index
174
     * @return array [$sql, [...$params]]
175
     */
176
    public function get_field_sql_and_params(int $index = 0): array {
177
        $fieldsql = $this->get_field_sql();
178
        $fieldparams = $this->get_field_params();
179
 
180
        // Shortcut if there aren't any parameters.
181
        if (empty($fieldparams)) {
182
            return [$fieldsql, $fieldparams];
183
        }
184
 
185
        // Simple callback for replacement of parameter names within filter SQL.
186
        $transform = function(string $param) use ($index): string {
187
            return "{$param}_{$index}";
188
        };
189
 
190
        $paramnames = array_keys($fieldparams);
191
        $sql = database::sql_replace_parameter_names($fieldsql, $paramnames, $transform);
192
 
193
        $params = [];
194
        foreach ($paramnames as $paramname) {
195
            $paramnametransform = $transform($paramname);
196
            $params[$paramnametransform] = $fieldparams[$paramname];
197
        }
198
 
199
        return [$sql, $params];
200
    }
201
 
202
    /**
203
     * Set the SQL expression for the field that is being filtered. It will be passed to the filter class
204
     *
205
     * @param string $sql
206
     * @param array $params
207
     * @return self
208
     */
209
    public function set_field_sql(string $sql, array $params = []): self {
210
        $this->fieldsql = $sql;
211
        $this->fieldparams = $params;
212
        return $this;
213
    }
214
 
215
    /**
216
     * Return available state of the filter for the current user
217
     *
218
     * @return bool
219
     */
220
    public function get_is_available(): bool {
221
        return $this->available;
222
    }
223
 
224
    /**
225
     * Conditionally set whether the filter is available. For instance the filter may be added to a report with the
226
     * expectation that only some users are able to see it
227
     *
228
     * @param bool $available
229
     * @return self
230
     */
231
    public function set_is_available(bool $available): self {
232
        $this->available = $available;
233
        return $this;
234
    }
235
 
236
    /**
237
     * Set deprecated state of the filter, in which case it will still be shown when already present in existing reports but
238
     * won't be available for selection in the report editor
239
     *
240
     * @param string $deprecatedmessage
241
     * @return self
242
     */
243
    public function set_is_deprecated(string $deprecatedmessage = ''): self {
244
        $this->deprecated = true;
245
        $this->deprecatedmessage = $deprecatedmessage;
246
        return $this;
247
    }
248
 
249
    /**
250
     * Return deprecated state of the filter
251
     *
252
     * @return bool
253
     */
254
    public function get_is_deprecated(): bool {
255
        return $this->deprecated;
256
    }
257
 
258
    /**
259
     * Return deprecated message of the filter
260
     *
261
     * @return string
262
     */
263
    public function get_is_deprecated_message(): string {
264
        return $this->deprecatedmessage;
265
    }
266
 
267
    /**
268
     * Set the options for the filter in the format that the filter class expected (e.g. the "select" filter expects an array)
269
     *
270
     * This method should only be used if the options do not require any calculations/queries, in which
271
     * case {@see set_options_callback} should be used. For performance, {@see get_string} shouldn't be used either, use of
272
     * {@see lang_string} is instead encouraged
273
     *
274
     * @param mixed $options
275
     * @return self
276
     */
277
    public function set_options($options): self {
278
        $this->options = $options;
279
        return $this;
280
    }
281
 
282
    /**
283
     * Set the options for the filter to be returned by a callback (that receives no arguments) in the format that the filter
284
     * class expects
285
     *
286
     * @param callable $callback
287
     * @return self
288
     */
289
    public function set_options_callback(callable $callback): self {
290
        $this->options = $callback;
291
        return $this;
292
    }
293
 
294
    /**
295
     * Get the options for the filter, returning via the the previously set options or generated via defined options callback
296
     *
297
     * @return mixed
298
     */
299
    public function get_options() {
300
        if (is_callable($this->options)) {
301
            $callable = $this->options;
302
            $this->options = ($callable)();
303
        }
304
        return $this->options;
305
    }
306
 
307
    /**
308
     * Set a limited subset of operators that should be used for the filter, refer to each filter class to find defined
309
     * operator constants
310
     *
311
     * @param array $limitoperators Simple array of operator values
312
     * @return self
313
     */
314
    public function set_limited_operators(array $limitoperators): self {
315
        $this->limitoperators = $limitoperators;
316
        return $this;
317
    }
318
 
319
    /**
320
     * Filter given operators to include only those previously defined by {@see set_limited_operators}
321
     *
322
     * @param array $operators All operators as defined by the filter class
323
     * @return array
324
     */
325
    public function restrict_limited_operators(array $operators): array {
326
        if (empty($this->limitoperators)) {
327
            return $operators;
328
        }
329
 
330
        return array_intersect_key($operators, array_flip($this->limitoperators));
331
    }
332
 
333
    /**
334
     * Set filter persistent
335
     *
336
     * @param filter_model $persistent
337
     * @return self
338
     */
339
    public function set_persistent(filter_model $persistent): self {
340
        $this->persistent = $persistent;
341
        return $this;
342
    }
343
 
344
    /**
345
     * Return filter persistent
346
     *
347
     * @return filter_model|null
348
     */
349
    public function get_persistent(): ?filter_model {
350
        return $this->persistent ?? null;
351
    }
352
}