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;
20
 
21
use coding_exception;
22
use core_reportbuilder\local\helpers\report;
23
use core_reportbuilder\local\models\column as column_model;
24
use core_reportbuilder\local\models\filter as filter_model;
25
use core_reportbuilder\local\report\base;
26
use core_reportbuilder\local\report\column;
27
use core_reportbuilder\local\report\filter;
28
 
29
/**
30
 * Class datasource
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
abstract class datasource extends base {
37
 
38
    /** @var float[] $elementsmodified Track the time elements of specific reports have been added, updated, removed */
39
    private static $elementsmodified = [];
40
 
41
    /** @var array $activecolumns */
42
    private $activecolumns;
43
 
44
    /** @var array $activefilters */
45
    private $activefilters;
46
 
47
    /** @var array $activeconditions */
48
    private $activeconditions;
49
 
50
    /**
51
     * Add default datasource columns to the report
52
     *
53
     * Uses column data returned by the source {@see get_default_columns} and {@see get_default_column_sorting} methods
54
     *
55
     * @throws coding_exception If default column sorting refers to an invalid column
56
     */
57
    public function add_default_columns(): void {
58
        $reportid = $this->get_report_persistent()->get('id');
59
 
60
        // Retrieve default column sorting, and track index of both sorted/non-sorted columns.
61
        $columnidentifiers = $this->get_default_columns();
62
 
63
        $defaultcolumnsorting = $this->get_default_column_sorting();
64
        $defaultcolumnsortinginvalid = array_diff_key($defaultcolumnsorting,
65
            array_fill_keys($columnidentifiers, 1));
66
 
67
        if (count($defaultcolumnsortinginvalid) > 0) {
68
            throw new coding_exception('Invalid column name', array_key_first($defaultcolumnsortinginvalid));
69
        }
70
 
71
        $columnnonsortingindex = count($defaultcolumnsorting) + 1;
72
 
73
        foreach ($columnidentifiers as $uniqueidentifier) {
74
            $column = report::add_report_column($reportid, $uniqueidentifier);
75
 
76
            // After adding the column, toggle sorting according to defaults provided by the datasource.
77
            $sortorder = array_search($uniqueidentifier, array_keys($defaultcolumnsorting));
78
            if ($sortorder !== false) {
79
                $column->set_many([
80
                    'sortenabled' => true,
81
                    'sortdirection' => $defaultcolumnsorting[$uniqueidentifier],
82
                    'sortorder' => $sortorder + 1,
83
                ])->update();
84
            } else if (!empty($defaultcolumnsorting)) {
85
                $column->set('sortorder', $columnnonsortingindex++)->update();
86
            }
87
        }
88
    }
89
 
90
    /**
91
     * Return the default columns that will be added to the report upon creation, by {@see add_default_columns}
92
     *
93
     * @return string[]
94
     */
95
    abstract public function get_default_columns(): array;
96
 
97
    /**
98
     * Return the default column sorting that will be set for the report upon creation, by {@see add_default_columns}
99
     *
100
     * When overriding this method in child classes, column identifiers specified must refer to default columns returned from
101
     * the {@see get_default_columns} method
102
     *
103
     * @return int[] array [column identifier => SORT_ASC/SORT_DESC]
104
     */
105
    public function get_default_column_sorting(): array {
106
        return [];
107
    }
108
 
109
    /**
110
     * Override parent method, returning only those columns specifically added to the custom report (rather than all that are
111
     * available)
112
     *
113
     * @return column[]
114
     */
115
    public function get_active_columns(): array {
116
        $reportid = $this->get_report_persistent()->get('id');
117
 
118
        // Determine whether we already retrieved the columns since the report was last modified.
119
        self::$elementsmodified += [$reportid => -1];
120
        if ($this->activecolumns !== null && $this->activecolumns['builttime'] > self::$elementsmodified[$reportid]) {
121
            return $this->activecolumns['values'];
122
        }
123
 
124
        $this->activecolumns = ['builttime' => microtime(true), 'values' => []];
125
 
126
        $activecolumns = column_model::get_records(['reportid' => $reportid], 'columnorder');
127
        foreach ($activecolumns as $index => $column) {
128
            $instance = $this->get_column($column->get('uniqueidentifier'));
129
 
130
            // Ensure the column is still present and available.
131
            if ($instance !== null && $instance->get_is_available()) {
132
                if ($instance->get_is_deprecated()) {
133
                    debugging("The column '{$instance->get_unique_identifier()}' is deprecated, please do not use it any more." .
134
                        " {$instance->get_is_deprecated_message()}", DEBUG_DEVELOPER);
135
                }
136
 
1441 ariadna 137
                $columnaggregation = $column->get('aggregation');
138
 
1 efrain 139
                // We should clone the report column to ensure if it's added twice to a report, each operates independently.
140
                $this->activecolumns['values'][] = clone $instance
141
                    ->set_index($index)
142
                    ->set_persistent($column)
1441 ariadna 143
                    ->set_aggregation($columnaggregation, $instance->get_aggregation_options($columnaggregation));
1 efrain 144
            }
145
        }
146
 
147
        return $this->activecolumns['values'];
148
    }
149
 
150
    /**
151
     * Add default datasource filters to the report
152
     *
153
     * This method is optional and can be called when the report is created to add the default filters defined in the
154
     * selected datasource.
155
     */
156
    public function add_default_filters(): void {
157
        $reportid = $this->get_report_persistent()->get('id');
158
        $filteridentifiers = $this->get_default_filters();
159
        foreach ($filteridentifiers as $uniqueidentifier) {
160
            report::add_report_filter($reportid, $uniqueidentifier);
161
        }
162
    }
163
 
164
    /**
165
     * Return the filters that will be added to the report once is created
166
     *
167
     * @return string[]
168
     */
169
    abstract public function get_default_filters(): array;
170
 
171
    /**
172
     * Override parent method, returning only those filters specifically added to the custom report (rather than all that are
173
     * available)
174
     *
175
     * @return filter[]
176
     */
177
    public function get_active_filters(): array {
178
        $reportid = $this->get_report_persistent()->get('id');
179
 
180
        // Determine whether we already retrieved the filters since the report was last modified.
181
        self::$elementsmodified += [$reportid => -1];
182
        if ($this->activefilters !== null && $this->activefilters['builttime'] > self::$elementsmodified[$reportid]) {
183
            return $this->activefilters['values'];
184
        }
185
 
186
        $this->activefilters = ['builttime' => microtime(true), 'values' => []];
187
 
188
        $activefilters = filter_model::get_filter_records($reportid, 'filterorder');
189
        foreach ($activefilters as $filter) {
190
            $instance = $this->get_filter($filter->get('uniqueidentifier'));
191
 
192
            // Ensure the filter is still present and available.
193
            if ($instance !== null && $instance->get_is_available()) {
194
                if ($instance->get_is_deprecated()) {
195
                    debugging("The filter '{$instance->get_unique_identifier()}' is deprecated, please do not use it any more." .
196
                        " {$instance->get_is_deprecated_message()}", DEBUG_DEVELOPER);
197
                }
198
 
1441 ariadna 199
                $this->activefilters['values'][$instance->get_unique_identifier()] = $instance->set_persistent($filter);
1 efrain 200
            }
201
        }
202
 
203
        return $this->activefilters['values'];
204
    }
205
 
206
    /**
207
     * Add conditions from the given entity name to be available to use in a custom report
208
     *
209
     * Wildcard matching is supported with '*' in both $include and $exclude, e.g. ['customfield*']
210
     *
211
     * @param string $entityname
212
     * @param string[] $include Include only these conditions, if omitted then include all
213
     * @param string[] $exclude Exclude these conditions, if omitted then exclude none
214
     * @throws coding_exception If both $include and $exclude are non-empty
215
     */
216
    final protected function add_conditions_from_entity(string $entityname, array $include = [], array $exclude = []): void {
217
        if (!empty($include) && !empty($exclude)) {
218
            throw new coding_exception('Cannot specify conditions to include and exclude simultaneously');
219
        }
220
 
221
        $entity = $this->get_entity($entityname);
222
 
223
        // Retrieve filtered conditions from entity, respecting given $include/$exclude parameters.
224
        $conditions = array_filter($entity->get_conditions(), function(filter $condition) use ($include, $exclude): bool {
225
            if (!empty($include)) {
226
                return $this->report_element_search($condition->get_name(), $include);
227
            }
228
 
229
            if (!empty($exclude)) {
230
                return !$this->report_element_search($condition->get_name(), $exclude);
231
            }
232
 
233
            return true;
234
        });
235
 
236
        foreach ($conditions as $condition) {
237
            $this->add_condition($condition);
238
        }
239
    }
240
 
241
    /**
242
     * Add default datasource conditions to the report
243
     *
244
     * This method is optional and can be called when the report is created to add the default conditions defined in the
245
     * selected datasource.
246
     */
247
    public function add_default_conditions(): void {
248
        $reportid = $this->get_report_persistent()->get('id');
249
        $conditionidentifiers = $this->get_default_conditions();
250
        foreach ($conditionidentifiers as $uniqueidentifier) {
251
            report::add_report_condition($reportid, $uniqueidentifier);
252
        }
253
 
254
        // Set the default condition values if they have been set in the datasource.
255
        $this->set_condition_values($this->get_default_condition_values());
256
    }
257
 
258
    /**
259
     * Return the conditions that will be added to the report once is created
260
     *
261
     * @return string[]
262
     */
263
    abstract public function get_default_conditions(): array;
264
 
265
    /**
266
     * Return the default condition values that will be added to the report once is created
267
     *
268
     * For any of the default conditions returned by the method {@see get_default_conditions} is
269
     * possible to set the initial values.
270
     *
271
     * @return array
272
     */
273
    public function get_default_condition_values(): array {
274
        return [];
275
    }
276
 
277
    /**
278
     * Override parent method, returning only those conditions specifically added to the custom report (rather than all that are
279
     * available)
280
     *
1441 ariadna 281
     * @param bool $checkavailable
1 efrain 282
     * @return filter[]
283
     */
1441 ariadna 284
    public function get_active_conditions(bool $checkavailable = true): array {
1 efrain 285
        $reportid = $this->get_report_persistent()->get('id');
286
 
287
        // Determine whether we already retrieved the conditions since the report was last modified.
288
        self::$elementsmodified += [$reportid => -1];
289
        if ($this->activeconditions !== null && $this->activeconditions['builttime'] > self::$elementsmodified[$reportid]) {
290
            return $this->activeconditions['values'];
291
        }
292
 
293
        $this->activeconditions = ['builttime' => microtime(true), 'values' => []];
294
 
295
        $activeconditions = filter_model::get_condition_records($reportid, 'filterorder');
296
        foreach ($activeconditions as $condition) {
297
            $instance = $this->get_condition($condition->get('uniqueidentifier'));
298
 
1441 ariadna 299
            // Ensure the condition is still present and available (if checking available status).
300
            if ($instance !== null && (!$checkavailable || $instance->get_is_available())) {
1 efrain 301
                if ($instance->get_is_deprecated()) {
302
                    debugging("The condition '{$instance->get_unique_identifier()}' is deprecated, please do not use it any more." .
303
                        " {$instance->get_is_deprecated_message()}", DEBUG_DEVELOPER);
304
                }
305
 
1441 ariadna 306
                $this->activeconditions['values'][$instance->get_unique_identifier()] = $instance->set_persistent($condition);
1 efrain 307
            }
308
        }
309
 
310
        return $this->activeconditions['values'];
311
    }
312
 
313
    /**
314
     * Adds all columns/filters/conditions from the given entity to the report at once
315
     *
316
     * @param string $entityname
317
     * @param string[] $limitcolumns Include only these columns
318
     * @param string[] $limitfilters Include only these filters
319
     * @param string[] $limitconditions Include only these conditions
320
     */
321
    final protected function add_all_from_entity(
322
        string $entityname,
323
        array $limitcolumns = [],
324
        array $limitfilters = [],
325
        array $limitconditions = [],
326
    ): void {
327
        $this->add_columns_from_entity($entityname, $limitcolumns);
328
        $this->add_filters_from_entity($entityname, $limitfilters);
329
        $this->add_conditions_from_entity($entityname, $limitconditions);
330
    }
331
 
332
    /**
333
     * Adds all columns/filters/conditions from all the entities added to the report at once
1441 ariadna 334
     *
335
     * @param string[] $entitynames If specified, then only these entity elements are added (otherwise all)
1 efrain 336
     */
1441 ariadna 337
    final protected function add_all_from_entities(array $entitynames = []): void {
1 efrain 338
        foreach ($this->get_entities() as $entity) {
1441 ariadna 339
            $entityname = $entity->get_entity_name();
340
            if (!empty($entitynames) && array_search($entityname, $entitynames) === false) {
341
                continue;
342
            }
343
            $this->add_all_from_entity($entityname);
1 efrain 344
        }
345
    }
346
 
347
    /**
348
     * Indicate that report elements have been modified, e.g. columns/filters/conditions have been added, removed or updated
349
     *
350
     * @param int $reportid
351
     */
352
    final public static function report_elements_modified(int $reportid): void {
353
        self::$elementsmodified[$reportid] = microtime(true);
354
    }
355
}