You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/apis/plugintypes/qbank/filters.md
+39-50Lines changed: 39 additions & 50 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,31 +9,26 @@ documentationDraft: true
9
9
---
10
10
11
11
<Since
12
-
version="4.3"
13
-
issueNumber="MDL-72321"
12
+
version="4.3"
13
+
issueNumber="MDL-72321"
14
14
/>
15
15
16
-
Question bank plugins allow you define additional filters. These can be used when viewing the question bank, and are included
17
-
in the URL so that a filtered view of the question bank can be shared. They are also used when defining the criteria for adding
18
-
random questions to a quiz.
16
+
Question bank plugins allow you define additional filters. These can be used when viewing the question bank, and are included in the URL so that a filtered view of the question bank can be shared. They are also used when defining the criteria for adding random questions to a quiz.
19
17
20
18
## Creating a new filter condition
21
19
22
20
A filter condition consists of two parts - the backend "condition" PHP class, and the frontend "filter" JavaScript class.
23
21
24
-
The "condition" class defines the general properties of the filter - its name, various options, and how it is applied to the
25
-
question bank query.
22
+
The "condition" class defines the general properties of the filter - its name, various options, and how it is applied to the question bank query.
26
23
The "filter" class defines how the filter is displayed in the UI, and how values selected in the UI are passed back to the condition.
27
24
28
25
Each new filter condition must define a new "condition" class in the qbank plugin based on `core_question\local\bank\condition`.
29
26
By default this will use the `core/datafilter/filtertype` "filter" class, although this can be overridden too if required.
30
27
31
28
### Basic example
32
29
33
-
This outlines the bare minimum required to implement a new filter condition. This will give you a field that allows you to
34
-
enter keywords and add them to a list of selected search terms, the filter the questions by that list of terms.
35
-
This assumes that you already have the basic framework of a qbank plugin in place. For real-world examples,
36
-
look for classes that extend `core_question\local\bank\condition`.
30
+
This outlines the bare minimum required to implement a new filter condition. This will give you a field that allows you to enter keywords and add them to a list of selected search terms, the filter the questions by that list of terms.
31
+
This assumes that you already have the basic framework of a qbank plugin in place. For real-world examples, look for classes that extend `core_question\local\bank\condition`.
37
32
38
33
Create a `condition` class within your plugin's namespace. For a plugin called `qbank_myplugin` this would look something like:
39
34
@@ -69,8 +64,7 @@ public function get_name(): string {
69
64
}
70
65
```
71
66
72
-
Define `get_condition_key()`, which returns a unique machine-readable ID for this filter condition, used when passing the filter
73
-
as a parameter.
67
+
Define `get_condition_key()`, which returns a unique machine-readable ID for this filter condition, used when passing the filter as a parameter.
74
68
75
69
```php title="Define the condition key"
76
70
public function get_condition_key(): string {
@@ -79,12 +73,9 @@ public function get_condition_key(): string {
79
73
```
80
74
81
75
To actually filter the results, define `build_query_from_filter()` which returns an SQL `WHERE` condition, and an array of parameters.
82
-
The `$filter` parameter receives an array with a `'values'` key, containing an array of the selected values, and a `'jointype'` key,
83
-
containing one of the `JOINTTYPE_ANY`, `JOINTYPE_ALL` or `JOINTYPE_NONE` constants. Use these to build your condition as required.
76
+
The `$filter` parameter receives an array with a `'values'` key, containing an array of the selected values, and a `'jointype'` key, containing one of the `JOINTTYPE_ANY`, `JOINTYPE_ALL` or `JOINTYPE_NONE` constants. Use these to build your condition as required.
84
77
85
-
The conditions from each filter are combined with the query in
The conditions from each filter are combined with the query in [`core_question\local\bank\view::build_query()`](https://github.com/moodle/moodle/blob/c741492c38b9945abbfc7e90dfe8f943279f8265/question/classes/local/bank/view.php#L733)
88
79
89
80
```php title="Filter questions"
90
81
public function build_query_from_filter(array $filter): array {
@@ -109,15 +100,13 @@ public function build_query_from_filter(array $filter): array {
109
100
}
110
101
```
111
102
112
-
Following this pattern with your own fields and options will give you a basic functional filter. Most filters will require
113
-
more complex functionality, which can be achieved through additional methods.
103
+
Following this pattern with your own fields and options will give you a basic functional filter. Most filters will require more complex functionality, which can be achieved through additional methods.
114
104
115
105
### Additional options
116
106
117
107
#### Pre-defined values
118
108
119
-
To define the list of possible filter values, define `get_initial_values()`, which returns an array of `['value', 'title']` for each
120
-
option. These will then be searchable and selectable in the autocomplete field.
109
+
To define the list of possible filter values, define `get_initial_values()`, which returns an array of `['value', 'title']` for each option. These will then be searchable and selectable in the autocomplete field.
121
110
122
111
```php title="Define initial filter values"
123
112
public function get_initial_values(): string {
@@ -146,8 +135,7 @@ public function allow_custom(): bool {
146
135
147
136
#### Restrict join types
148
137
149
-
Not all join types are relevant to all filters. If each question will only match one of the selected values, it does not make
150
-
sense to allow `JOINTYPE_ALL`. Define `get_join_list()` and return an array of the applicable join types.
138
+
Not all join types are relevant to all filters. If each question will only match one of the selected values, it does not make sense to allow `JOINTYPE_ALL`. Define `get_join_list()` and return an array of the applicable join types.
151
139
152
140
```php title="Define a restricted list of join types"
153
141
public function get_join_list(): array {
@@ -171,8 +159,7 @@ public function allow_multiple(): bool {
171
159
172
160
#### Allow empty values?
173
161
174
-
By default, conditions can be left empty, and therefore will not be included in the filter. To make it compulsory to select a
175
-
value for this condition when it is added, override `allow_empty()` to return false.
162
+
By default, conditions can be left empty, and therefore will not be included in the filter. To make it compulsory to select a value for this condition when it is added, override `allow_empty()` to return false.
176
163
177
164
```php title="Disable empty values"
178
165
public function allow_empty(): bool {
@@ -198,8 +185,7 @@ If this does not fit your filter's use case, you can tell your condition to use
198
185
199
186
You can either use a different core filter type from `/lib/amd/src/datafilter/filtertypes`, or define your own.
200
187
201
-
To tell your filter condition to use a different filter class, override the `get_filter_class()` method to return the namespaced
202
-
path to your JavaScript class.
188
+
To tell your filter condition to use a different filter class, override the `get_filter_class()` method to return the namespaced path to your JavaScript class.
203
189
204
190
```php title="Override the default filter class"
205
191
public function get_filter_class(): string {
@@ -208,37 +194,40 @@ public function get_filter_class(): string {
208
194
```
209
195
210
196
To create your own filter class, a new JavaScript file in your plugin under `amd/src/datafilter/filtertypes/myfilter.js`.
211
-
In this file, export a default class that extends `core/datafilter/filtertype`
212
-
(or another core filter type from `/lib/amd/src/datafilter/filtertypes`) and override the base methods as required.
213
-
For example, if your filter uses textual rather than numeric values, you can override `get values()` to return the raw values
In this file, export a default class that extends `core/datafilter/filtertype` (or another core filter type from `/lib/amd/src/datafilter/filtertypes`) and override the base methods as required.
198
+
For example, if your filter uses textual rather than numeric values, you can override `get values()` to return the raw values without running `parseInt()` (see [`qbank_viewquestiontype/datafilter/filtertypes/type`](https://github.com/moodle/moodle/blob/main/mod/quiz/tests/behat/editing_add_from_question_bank.feature)).
216
199
217
200
If you want a different UI for selecting your filter values instead of a single autocomplete, you can override `addValueSelector()`.
218
201
This also provides flexibility over how the values provided by `get_initial_values()` are used by the UI.
219
202
220
203
#### Filter options
221
204
222
-
If your condition supports additional options as to how the selected values are applied to the query, such as whether child
223
-
categories are included when parent categories are selected, you can define "Filter options".
205
+
If your condition supports additional options as to how the selected values are applied to the query, such as whether child categories are included when parent categories are selected, you can define "Filter options".
224
206
225
-
In your condition class, define `get_filteroptions()` which returns an object containing the current filter options. You will
226
-
probably want to add some code to the constructor to read in the current filter options, and some code the `build_query_from_filter()`
In your condition class, define `get_filteroptions()` which returns an object containing the current filter options. You will probably want to add some code to the constructor to read in the current filter options, and some code the `build_query_from_filter()` to use the option.
208
+
See [`qbank_managecategories\category_condition`](https://github.com/moodle/moodle/blob/main/question/bank/managecategories/classes/category_condition.php) as an example.
230
209
231
-
You JavaScript filter class will also need to support your filter options. Override the constructor an add additional code
232
-
for the UI required to set your filter options, and override `get filterOptions()` to return the current value for any options set
You JavaScript filter class will also need to support your filter options. Override the constructor an add additional code for the UI required to set your filter options, and override `get filterOptions()` to return the current value for any options set in this UI.
211
+
See [`qbank_managecategories/datafilter/filtertypes/categories`](https://github.com/moodle/moodle/blob/main/question/bank/managecategories/amd/src/datafilter/filtertypes/categories.js) as an example.
236
212
237
213
#### Context-sensitive configuration
238
214
239
-
You may want your filter to behave differently depending on where it is being displayed. In this case you can override the
240
-
constructor which receives the current `$qbank` view object, and extract some data that is used later on by your other methods.
215
+
You may want your filter to behave differently depending on where it is being displayed. In this case you can override the constructor which receives the current `$qbank` view object, and extract some data that is used later on by your other methods.
will find the context of the current page, and use that to control which tags are available in the filter.
217
+
For example, the [tag condition](https://github.com/moodle/moodle/blob/main/question/bank/tagquestion/classes/tag_condition.php) will find the context of the current page, and use that to control which tags are available in the filter.
218
+
219
+
#### Validation
220
+
221
+
<Since
222
+
version="5.0"
223
+
issueNumber="MDL-83862"
224
+
/>
225
+
226
+
Filters support standard [Client-side form validation](https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Forms/Form_validation).
227
+
The simplest way to implement this is to set validation properties on your inputs in the mustache template used by your `addValueSelector()` method.
228
+
229
+
If you need something more advanced, you can define a `validation()` method in your filter class. This is called when the "Apply filters" button is clicked, giving you the opportunity to inspect the current values of the filter, and perform validation checks.
230
+
If validation fails, you should display errors using the standard `setCustomValidity()` and `reportValidity()` methods on your filter's input elements, and return `false`.
231
+
See `core/datafilter/filtertypes/datetime` for an example.
232
+
233
+
This client-side validation is only to prevent invalid values being entered in the UI. You should also validate data received by the `build_query_from_filter()` method in your condition class, and throw exceptions in the event of validation failures.
0 commit comments