Skip to content

Commit 7f718a4

Browse files
committed
MDL-78520 Add question bank data mapper API guide
1 parent a68fbfc commit 7f718a4

File tree

2 files changed

+106
-0
lines changed

2 files changed

+106
-0
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
---
2+
title: Data mapper API for export and import
3+
tags:
4+
- Plugins
5+
- Question
6+
- qbank
7+
description: The data mapper API allows you to export and import additional question data stored by your plugin.
8+
documentationDraft: true
9+
---
10+
11+
If your question bank plugin stores additional data about a question, you can export and import that data along
12+
with the question by implementing the data mapper API. This is currently only supported by the Moodle XML export format.
13+
14+
To implement a data mapper for your plugin, create a new class that extends `\core_question\local\bank\data_mapper_base`,
15+
and return an instance from `get_data_mapper` in your `plugin_feature` class:
16+
17+
```php title="question/bank/example/classes/data_mapper.php"
18+
namespace qbank_example;
19+
20+
class data_mapper extends \core_question\local\bank\data_mapper_base {
21+
22+
}
23+
```
24+
25+
```php title="question/bank/example/classes/plugin_feature.php"
26+
namespace qbank_example;
27+
28+
class plugin_feature extends plugin_features_base {
29+
#[\Override]
30+
public function get_data_mapper(): data_mapper_base {
31+
return new data_mapper();
32+
}
33+
}
34+
```
35+
36+
The data mapper API provides two methods, `get_question_data` to return the additional data related to each question
37+
for export, and `save_question_data` to take imported data and save it against an imported question.
38+
39+
## get_question_data
40+
41+
`get_question_data` must return an multi-dimensional array, keyed by the ID of the question that data belongs to.
42+
Even if the plugin has no data to export for a given question, it should return that key with an empty array. Starting
43+
your `get_question_data` with `$questiondata = parent::get_question_data($questionids);` will give you an empty array
44+
for each question ID.
45+
46+
The resulting array should be in the format `$questiondata[$questionid][$itemidentifier] = [$key => $value]`, where
47+
`$itemidentifier` is some human-readable identifier for the data item you are exporting (such as shortname or idnumber),
48+
and the `[$key => $value]` array is a list of arbitrary data fields and values you want to export for that item.
49+
50+
```php title="question/bank/example/classes/data_mapper.php"
51+
#[\Override]
52+
public function get_question_data(array $questionids): array {
53+
global $DB;
54+
$questiondata = parent::get_question_data($questionids);
55+
foreach ($questionids as $questionid) {
56+
$exampleitem = $DB->get_record('qbank_example', ['questionid' => $questionid]);
57+
$questiondata[$questionid][$exampleitem->idnumber] = [
58+
'field1' => $exampleitem->field1,
59+
'field2' => $exampleitem->field2,
60+
];
61+
}
62+
return $questiondata;
63+
}
64+
```
65+
66+
## save_question_data
67+
68+
`save_question_data` will recieve the ID of the question that has been imported, and an array of additional data
69+
for this plugin in from the imported data. This will match the format of the `$questiondata[$questionid]` array
70+
from `get_question_data`. You can then do whatever processing is required to store this data against the newly
71+
imported question.
72+
73+
This function should return an array like `['error' => 'error messages', 'notice' => 'notice messages']` containing
74+
any messages that need to be reported from the import process, such as invalid data or non-existant fields.
75+
Starting your method with `$return = parent::save_question_data($questionid, $data);` will generate this array
76+
for you.
77+
78+
```php title="question/bank/example/classes/data_mapper.php"
79+
#[\Override]
80+
public function save_question_data(int $questionid, array $data): array {
81+
global $DB;
82+
$return = parent::save_question_data($questionid, $data);
83+
try {
84+
$validfields = ['field1', 'field2'];
85+
foreach ($data as $itemid => $itemdata) {
86+
$example = [
87+
'questionid' => $questionid,
88+
'idnumber' => $itemid,
89+
];
90+
foreach ($itemdata as $field => $value) {
91+
if (!in_array($field, $validfields)) {
92+
$return['notice'] .= "Skipped invalid field '{$field}' for question ID '{$questionid}'. ";
93+
continue;
94+
}
95+
$example[$field] = $value;
96+
}
97+
$DB->insert_record('qbank_example', $example);
98+
}
99+
} catch (\Throwable $e) {
100+
$return['error'] = $e->getMessage();
101+
}
102+
103+
return $questiondata;
104+
}
105+
```

docs/apis/plugintypes/qbank/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ Question bank plugins can extend the question bank in many ways, including:
2222
- Bulk actions
2323
- Navigation node (tabs)
2424
- Question preview additions (via callback)
25+
- [Map additional question data for import/export](./data-mapper.md)
2526

2627
The place to start implementing most of these is with a class `classes/plugin_features.php` in your plugin, that declares which features you want to add to the question bank. Until more documentation is written, looking at the examples of the plugins in Moodle core should give you a good idea what you need to do.

0 commit comments

Comments
 (0)