ILIAS  trunk Revision v11.0_alpha-1769-g99a433fe2dc
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
class.SurveyMultipleChoiceQuestion.php
Go to the documentation of this file.
1 <?php
2 
26 {
28 
29  public function __construct(
30  string $title = "",
31  string $description = "",
32  string $author = "",
33  string $questiontext = "",
34  int $owner = -1,
35  int $orientation = 0
36  ) {
37  global $DIC;
38 
39  $this->db = $DIC->database();
40  $this->lng = $DIC->language();
42 
43  $this->orientation = $orientation;
44  $this->categories = new SurveyCategories();
45  }
46 
47  public function getQuestionDataArray(int $id): array
48  {
49  $ilDB = $this->db;
50 
51  $result = $ilDB->queryF(
52  "SELECT svy_question.*, " . $this->getAdditionalTableName() . ".* FROM svy_question, " . $this->getAdditionalTableName() . " WHERE svy_question.question_id = %s AND svy_question.question_id = " . $this->getAdditionalTableName() . ".question_fi",
53  array('integer'),
54  array($id)
55  );
56  if ($result->numRows() === 1) {
57  return $ilDB->fetchAssoc($result);
58  }
59 
60  return array();
61  }
62 
63  public function loadFromDb(int $question_id): void
64  {
65  $ilDB = $this->db;
66 
67  $result = $ilDB->queryF(
68  "SELECT svy_question.*, " . $this->getAdditionalTableName() . ".* FROM svy_question LEFT JOIN " . $this->getAdditionalTableName() . " ON " . $this->getAdditionalTableName() . ".question_fi = svy_question.question_id WHERE svy_question.question_id = %s",
69  array('integer'),
70  array($question_id)
71  );
72  if ($result->numRows() === 1) {
73  $data = $ilDB->fetchAssoc($result);
74  $this->setId($data["question_id"]);
75  $this->setTitle((string) $data["title"]);
76  $this->label = (string) $data['label'];
77  $this->setDescription((string) $data["description"]);
78  $this->setObjId((int) $data["obj_fi"]);
79  $this->setAuthor((string) $data["author"]);
80  $this->setOwner((int) $data["owner_fi"]);
81  $this->setQuestiontext(ilRTE::_replaceMediaObjectImageSrc((string) $data["questiontext"], 1));
82  $this->setObligatory((bool) $data["obligatory"]);
83  $this->setComplete((bool) $data["complete"]);
84  $this->setOriginalId((int) $data["original_id"]);
85  $this->setOrientation((int) $data["orientation"]);
86  $this->use_min_answers = (bool) $data['use_min_answers'];
87  $this->nr_min_answers = (string) $data['nr_min_answers'];
88  $this->nr_max_answers = (string) $data['nr_max_answers'];
89 
90  $this->categories->flushCategories();
91  $result = $ilDB->queryF(
92  "SELECT svy_variable.*, svy_category.title, svy_category.neutral FROM svy_variable, svy_category WHERE svy_variable.question_fi = %s AND svy_variable.category_fi = svy_category.category_id ORDER BY sequence ASC",
93  array('integer'),
94  array($question_id)
95  );
96  if ($result->numRows() > 0) {
97  while ($data = $ilDB->fetchAssoc($result)) {
98  $this->categories->addCategory($data["title"], $data["other"], $data["neutral"], null, ($data['scale']) ?: ($data['sequence'] + 1));
99  }
100  }
101  }
102  parent::loadFromDb($question_id);
103  }
104 
105  public function isComplete(): bool
106  {
107  return (
108  $this->getTitle() !== '' &&
109  $this->getAuthor() !== '' &&
110  $this->getQuestiontext() !== '' &&
111  $this->categories->getCategoryCount()
112  );
113  }
114 
115  public function saveToDb(int $original_id = 0): int
116  {
117  $ilDB = $this->db;
118 
119  $affectedRows = parent::saveToDb($original_id);
120  if ($affectedRows === 1) {
121  $ilDB->manipulateF(
122  "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
123  array('integer'),
124  array($this->getId())
125  );
126  $ilDB->manipulateF(
127  "INSERT INTO " . $this->getAdditionalTableName() . " (question_fi, orientation, use_min_answers, nr_min_answers, nr_max_answers) VALUES (%s, %s, %s, %s, %s)",
128  array('integer', 'text', 'integer', 'integer', 'integer'),
129  array(
130  $this->getId(),
131  $this->getOrientation(),
132  ($this->use_min_answers) ? 1 : 0,
133  ($this->nr_min_answers > 0) ? $this->nr_min_answers : null,
134  ($this->nr_max_answers > 0) ? $this->nr_max_answers : null
135  )
136  );
137 
138  $this->saveCategoriesToDb();
139  }
140  return $affectedRows;
141  }
142 
143  public function saveCategoriesToDb(): void
144  {
145  $ilDB = $this->db;
146 
147  $affectedRows = $ilDB->manipulateF(
148  "DELETE FROM svy_variable WHERE question_fi = %s",
149  array('integer'),
150  array($this->getId())
151  );
152 
153  for ($i = 0; $i < $this->categories->getCategoryCount(); $i++) {
154  $cat = $this->categories->getCategory($i);
155  $category_id = $this->saveCategoryToDb($cat->title, $cat->neutral);
156  $next_id = $ilDB->nextId('svy_variable');
157  $affectedRows = $ilDB->manipulateF(
158  "INSERT INTO svy_variable (variable_id, category_fi, question_fi, value1, other, sequence, scale, tstamp) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)",
159  array('integer','integer','integer','float','integer','integer', 'integer','integer'),
160  array($next_id, $category_id, $this->getId(), ($i + 1), $cat->other, $i, ($cat->scale > 0) ? $cat->scale : null, time())
161  );
162  }
163  $this->saveCompletionStatus();
164  }
165 
166  public function toXML(
167  bool $a_include_header = true,
168  bool $obligatory_state = false
169  ): string {
170  $a_xml_writer = new ilXmlWriter();
171  $a_xml_writer->xmlHeader();
172  $this->insertXML($a_xml_writer, $a_include_header);
173  $xml = $a_xml_writer->xmlDumpMem(false);
174  if (!$a_include_header) {
175  $pos = strpos($xml, "?>");
176  $xml = substr($xml, $pos + 2);
177  }
178  return $xml;
179  }
180 
184  public function insertXML(
185  ilXmlWriter $a_xml_writer,
186  bool $a_include_header = true
187  ): void {
188  $attrs = array(
189  "id" => $this->getId(),
190  "title" => $this->getTitle(),
191  "type" => $this->getQuestionType(),
192  "obligatory" => $this->getObligatory()
193  );
194  $a_xml_writer->xmlStartTag("question", $attrs);
195 
196  $a_xml_writer->xmlElement("description", null, $this->getDescription());
197  $a_xml_writer->xmlElement("author", null, $this->getAuthor());
198  if (strlen($this->label ?? "")) {
199  $attrs = array(
200  "label" => $this->label,
201  );
202  } else {
203  $attrs = array();
204  }
205  $a_xml_writer->xmlStartTag("questiontext", $attrs);
206  $this->addMaterialTag($a_xml_writer, $this->getQuestiontext());
207  $a_xml_writer->xmlEndTag("questiontext");
208 
209  $a_xml_writer->xmlStartTag("responses");
210 
211  for ($i = 0; $i < $this->categories->getCategoryCount(); $i++) {
212  $attrs = array(
213  "id" => $i
214  );
215  if (strlen($this->categories->getCategory($i)->other ?? "")) {
216  $attrs['other'] = $this->categories->getCategory($i)->other;
217  }
218  if (strlen($this->categories->getCategory($i)->neutral ?? "")) {
219  $attrs['neutral'] = $this->categories->getCategory($i)->neutral;
220  }
221  if (strlen($this->categories->getCategory($i)->label ?? "")) {
222  $attrs['label'] = $this->categories->getCategory($i)->label;
223  }
224  if (strlen($this->categories->getCategory($i)->scale ?? "")) {
225  $attrs['scale'] = $this->categories->getCategory($i)->scale;
226  }
227  $a_xml_writer->xmlStartTag("response_multiple", $attrs);
228  $this->addMaterialTag($a_xml_writer, $this->categories->getCategory($i)->title);
229  $a_xml_writer->xmlEndTag("response_multiple");
230  }
231 
232  $a_xml_writer->xmlEndTag("responses");
233 
234  if (count($this->material)) {
235  if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $this->material["internal_link"], $matches)) {
236  $attrs = array(
237  "label" => $this->material["title"]
238  );
239  $a_xml_writer->xmlStartTag("material", $attrs);
240  $intlink = "il_" . IL_INST_ID . "_" . $matches[2] . "_" . $matches[3];
241  if (strcmp($matches[1], "") !== 0) {
242  $intlink = $this->material["internal_link"];
243  }
244  $a_xml_writer->xmlElement("mattext", null, $intlink);
245  $a_xml_writer->xmlEndTag("material");
246  }
247  }
248 
249  $a_xml_writer->xmlStartTag("metadata");
250  $a_xml_writer->xmlStartTag("metadatafield");
251  $a_xml_writer->xmlElement("fieldlabel", null, "orientation");
252  $a_xml_writer->xmlElement("fieldentry", null, $this->getOrientation());
253  $a_xml_writer->xmlEndTag("metadatafield");
254  $a_xml_writer->xmlStartTag("metadatafield");
255  $a_xml_writer->xmlElement("fieldlabel", null, "use_min_answers");
256  $a_xml_writer->xmlElement("fieldentry", null, $this->use_min_answers);
257  $a_xml_writer->xmlEndTag("metadatafield");
258  $a_xml_writer->xmlStartTag("metadatafield");
259  $a_xml_writer->xmlElement("fieldlabel", null, "nr_min_answers");
260  $a_xml_writer->xmlElement("fieldentry", null, $this->nr_min_answers);
261  $a_xml_writer->xmlEndTag("metadatafield");
262  $a_xml_writer->xmlStartTag("metadatafield");
263  $a_xml_writer->xmlElement("fieldlabel", null, "nr_max_answers");
264  $a_xml_writer->xmlElement("fieldentry", null, $this->nr_max_answers);
265  $a_xml_writer->xmlEndTag("metadatafield");
266  $a_xml_writer->xmlEndTag("metadata");
267 
268  $a_xml_writer->xmlEndTag("question");
269  }
270 
271  public function getQuestionType(): string
272  {
273  return "SurveyMultipleChoiceQuestion";
274  }
275 
276  public function getAdditionalTableName(): string
277  {
278  return "svy_qst_mc";
279  }
280 
281  public function getWorkingDataFromUserInput(array $post_data): array
282  {
283  $entered_value = $post_data[$this->getId() . "_value"] ?? "";
284  $data = array();
285  if (is_array($entered_value)) {
286  foreach ($entered_value as $idx => $value) {
287  $data[] = array("value" => $value,
288  "textanswer" => $post_data[$this->getId() . '_' . $value . '_other'] ?? ""
289  );
290  }
291  }
292  for ($i = 0; $i < $this->categories->getCategoryCount(); $i++) {
293  $cat = $this->categories->getCategory($i);
294  if ($cat->other) {
295  // #18212
296  if (!is_array($entered_value) || !in_array($i, $entered_value)) {
297  if (strlen($post_data[$this->getId() . "_" . $i . "_other"] ?? "")) {
298  $data[] = array("value" => $i,
299  "textanswer" => $post_data[$this->getId() . '_' . $i . '_other'] ?? "",
300  "uncheck" => true
301  );
302  }
303  }
304  }
305  }
306  return $data;
307  }
308 
312  public function checkUserInput(
313  array $post_data,
314  int $survey_id
315  ): string {
316  $entered_value = (array) ($post_data[$this->getId() . "_value"] ?? []);
317  if (!$this->getObligatory() && (count($entered_value) === 0)) {
318  return "";
319  }
320 
321  if ($this->use_min_answers && $this->nr_min_answers > 0 && $this->nr_max_answers > 0 && $this->nr_min_answers == $this->nr_max_answers && count($entered_value) !== (int) $this->nr_max_answers) {
322  return sprintf($this->lng->txt("err_no_exact_answers"), $this->nr_min_answers);
323  }
324  if ($this->use_min_answers && $this->nr_min_answers > 0 && count($entered_value) < $this->nr_min_answers) {
325  return sprintf($this->lng->txt("err_no_min_answers"), $this->nr_min_answers);
326  }
327  if ($this->use_min_answers && $this->nr_max_answers > 0 && count($entered_value) > $this->nr_max_answers) {
328  return sprintf($this->lng->txt("err_no_max_answers"), $this->nr_max_answers);
329  }
330  if (count($entered_value) == 0) {
331  return $this->lng->txt("question_mr_not_checked");
332  }
333  for ($i = 0; $i < $this->categories->getCategoryCount(); $i++) {
334  $cat = $this->categories->getCategory($i);
335  if ($cat->other) {
336  if (in_array($i, $entered_value)) {
337  if (array_key_exists($this->getId() . "_" . $i . "_other", $post_data) && !strlen($post_data[$this->getId() . "_" . $i . "_other"] ?? "")) {
338  return $this->lng->txt("question_mr_no_other_answer");
339  }
340  } elseif (strlen($post_data[$this->getId() . "_" . $i . "_other"] ?? "")) {
341  return $this->lng->txt("question_mr_no_other_answer_checked");
342  }
343  }
344  }
345  return "";
346  }
347 
348  public function saveUserInput(
349  array $post_data,
350  int $active_id,
351  bool $a_return = false
352  ): ?array {
353  $ilDB = $this->db;
354 
355  if ($a_return) {
356  $return_data = array();
357  }
358  if (is_array($post_data[$this->getId() . "_value"] ?? null)) {
359  foreach ($post_data[$this->getId() . "_value"] as $entered_value) {
360  if (strlen($entered_value ?? "") > 0) {
361  if (!$a_return) {
362  $next_id = $ilDB->nextId('svy_answer');
363 
364  #20216
365  $fields = array();
366  $fields['answer_id'] = array("integer", $next_id);
367  $fields['question_fi'] = array("integer", $this->getId());
368  $fields['active_fi'] = array("integer", $active_id);
369  $fields['value'] = array("float", (strlen($entered_value ?? "")) ? $entered_value : null);
370  $fields['textanswer'] = array("clob", isset($post_data[$this->getId() . "_" . $entered_value . "_other"]) ? $this->stripSlashesAddSpaceFallback($post_data[$this->getId() . "_" . $entered_value . "_other"]) : null);
371  $fields['tstamp'] = array("integer", time());
372 
373  $affectedRows = $ilDB->insert("svy_answer", $fields);
374  } else {
375  $return_data[] = array("value" => $entered_value,
376  "textanswer" => $post_data[$this->getId() . "_" . $entered_value . "_other"] ?? "");
377  }
378  }
379  }
380  }
381  if ($a_return) {
382  return $return_data;
383  }
384  return null;
385  }
386 
387  public function importAdditionalMetadata(array $a_meta): void
388  {
389  foreach ($a_meta as $key => $value) {
390  switch ($value["label"]) {
391  case "orientation":
392  $this->setOrientation($value["entry"]);
393  break;
394  case "use_min_answers":
395  $this->use_min_answers = $value["entry"];
396  break;
397  case "nr_min_answers":
398  $this->nr_min_answers = $value["entry"];
399  break;
400  case "nr_max_answers":
401  $this->nr_max_answers = $value["entry"];
402  break;
403  }
404  }
405  }
406 
407  public function importResponses(array $a_data): void
408  {
409  foreach ($a_data as $id => $data) {
410  $categorytext = "";
411  foreach ($data["material"] as $material) {
412  $categorytext .= $material["text"];
413  }
414  $this->categories->addCategory(
415  $categorytext,
416  strlen($data['other'] ?? "") ? $data['other'] : 0,
417  strlen($data['neutral'] ?? "") ? $data['neutral'] : 0,
418  strlen($data['label'] ?? "") ? $data['label'] : null,
419  strlen($data['scale'] ?? "") ? $data['scale'] : null
420  );
421  }
422  }
423 
424  public function usableForPrecondition(): bool
425  {
426  return true;
427  }
428 
429  public function getAvailableRelations(): array
430  {
431  return array("=", "<>");
432  }
433 
434  public function getPreconditionOptions(): array
435  {
436  $options = array();
437  for ($i = 0; $i < $this->categories->getCategoryCount(); $i++) {
438  $category = $this->categories->getCategory($i);
439  $options[$category->scale - 1] = $category->scale . " - " . $category->title;
440  }
441  return $options;
442  }
443 
444  public function getPreconditionSelectValue(
445  string $default,
446  string $title,
447  string $variable
448  ): ?ilFormPropertyGUI {
449  $step3 = new ilSelectInputGUI($title, $variable);
450  $options = $this->getPreconditionOptions();
451  $step3->setOptions($options);
452  $step3->setValue($default);
453  return $step3;
454  }
455 
456  public function getPreconditionValueOutput(
457  string $value
458  ): string {
459  // #18136
460  $category = $this->categories->getCategoryForScale((int) $value + 1);
461 
462  // #17895 - see getPreconditionOptions()
463  return $category->scale .
464  " - " .
465  ((strlen($category->title ?? "")) ? $category->title : $this->lng->txt('other_answer'));
466  }
467 
468  public function getCategories(): SurveyCategories
469  {
470  return $this->categories;
471  }
472 
473  public static function getMaxSumScore(int $survey_id): int
474  {
475  global $DIC;
476 
477  // we need sum of scale values of multiple choice questions (type 1)
478  $db = $DIC->database();
479  $set = $db->queryF(
480  "SELECT SUM(scale) sum_sum_score FROM svy_svy_qst sq " .
481  "JOIN svy_question q ON (sq.question_fi = q.question_id) " .
482  "JOIN svy_variable v ON (v.question_fi = q.question_id) " .
483  "WHERE sq.survey_fi = %s AND q.questiontype_fi = %s ",
484  ["integer", "integer"],
485  [$survey_id, 1]
486  );
487  $rec = $db->fetchAssoc($set);
488  return (int) $rec["sum_sum_score"];
489  }
490 }
static _replaceMediaObjectImageSrc(string $a_text, int $a_direction=0, string $nic='')
Replaces image source from mob image urls with the mob id or replaces mob id with the correct image s...
setQuestiontext(string $questiontext="")
saveCompletionStatus(int $original_id=0)
Saves the complete flag to the database.
const IL_INST_ID
Definition: constants.php:40
insertXML(ilXmlWriter $a_xml_writer, bool $a_include_header=true)
Adds the question XML to a given XMLWriter object.
This class represents a selection list property in a property form.
setObligatory(bool $obligatory=true)
fetchAssoc(ilDBStatement $statement)
setOriginalId(?int $original_id)
setComplete(bool $a_complete)
saveCategoryToDb(string $categorytext, int $neutral=0)
Saves a category to the database.
__construct(string $title="", string $description="", string $author="", string $questiontext="", int $owner=-1, int $orientation=0)
stripSlashesAddSpaceFallback(string $a_str)
Strip slashes with add space fallback, see https://mantis.ilias.de/view.php?id=19727 and https://mant...
xmlEndTag(string $tag)
Writes an endtag.
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
setOrientation(int $orientation=0)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setAuthor(string $author="")
addMaterialTag(ilXmlWriter $a_xml_writer, string $a_material, bool $close_material_tag=true, bool $add_mobs=true, ?array $a_attrs=null)
Creates an XML material tag from a plain text or xhtml text.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
toXML(bool $a_include_header=true, bool $obligatory_state=false)
getPreconditionSelectValue(string $default, string $title, string $variable)
global $DIC
Definition: shib_login.php:22
setTitle(string $title="")
queryF(string $query, array $types, array $values)
saveUserInput(array $post_data, int $active_id, bool $a_return=false)
setOwner(int $owner=0)
__construct(Container $dic, ilPlugin $plugin)
xmlStartTag(string $tag, ?array $attrs=null, bool $empty=false, bool $encode=true, bool $escape=true)
Writes a starttag.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
xmlElement(string $tag, $attrs=null, $data=null, $encode=true, $escape=true)
Writes a basic element (no children, just textual content)
setDescription(string $description="")
setObjId(int $obj_id=0)
Set the reference(?) id of the container object.
checkUserInput(array $post_data, int $survey_id)