ILIAS  release_8 Revision v8.23
class.SurveyMatrixQuestion.php
Go to the documentation of this file.
1 <?php
2 
25 {
28  // First bipolar adjective for ordinal matrix questions
29  public string $bipolar_adjective1 = "";
30  // Second bipolar adjective for ordinal matrix questions
31  public string $bipolar_adjective2 = "";
32  // Enable state of separators for matrix columns
33  public bool $columnSeparators = false;
34  // Enable state of separators for matrix rows
35  public bool $rowSeparators = false;
36  // Enable state of a separator for the neutral column
37  public bool $neutralColumnSeparator = false;
38  public array $layout;
39  // Use placeholders for the column titles
40  public bool $columnPlaceholders = false;
41  public bool $legend = false;
42  public bool $singleLineRowCaption = false;
43  public bool $repeatColumnHeader = false;
44 
55  public int $subtype;
56 
57  public function __construct(
58  string $title = "",
59  string $description = "",
60  string $author = "",
61  string $questiontext = "",
62  int $owner = -1
63  ) {
64  global $DIC;
65 
66  $this->user = $DIC->user();
67  $this->db = $DIC->database();
69 
70  $this->subtype = 0;
71  $this->columns = new SurveyCategories();
72  $this->rows = new SurveyCategories();
73  $this->bipolar_adjective1 = "";
74  $this->bipolar_adjective2 = "";
75  $this->rowSeparators = 0;
76  $this->columnSeparators = 0;
77  $this->neutralColumnSeparator = 1;
78  }
79 
80  public function getColumnCount(): int
81  {
82  return $this->columns->getCategoryCount();
83  }
84 
85  public function removeColumn(int $index): void
86  {
87  $this->columns->removeCategory($index);
88  }
89 
93  public function removeColumns(array $array): void
94  {
95  $this->columns->removeCategories($array);
96  }
97 
98  public function removeColumnWithName(string $name): void
99  {
100  $this->columns->removeCategoryWithName($name);
101  }
102 
103  public function getColumns(): SurveyCategories
104  {
105  return $this->columns;
106  }
107 
108  public function getColumn(int $index): ?ilSurveyCategory
109  {
110  return $this->columns->getCategory($index);
111  }
112 
113  public function getColumnForScale(int $scale): ?ilSurveyCategory
114  {
115  return $this->columns->getCategoryForScale($scale);
116  }
117 
118  public function getColumnIndex(string $name): int
119  {
120  return $this->columns->getCategoryIndex($name);
121  }
122 
123  public function flushColumns(): void
124  {
125  $this->columns->flushCategories();
126  }
127 
128  public function getRowCount(): int
129  {
130  return $this->rows->getCategoryCount();
131  }
132 
133  public function addRow(
134  string $a_text,
135  string $a_other,
136  string $a_label
137  ): void {
138  $this->rows->addCategory($a_text, (int) $a_other, 0, $a_label);
139  }
140 
141  public function addRowAtPosition(
142  string $a_text,
143  string $a_other,
144  int $a_position
145  ): void {
146  $this->rows->addCategoryAtPosition($a_text, $a_position, $a_other);
147  }
148 
149  public function flushRows(): void
150  {
151  $this->rows = new SurveyCategories();
152  }
153 
154  public function getRow(int $a_index): ?ilSurveyCategory
155  {
156  return $this->rows->getCategory($a_index);
157  }
158 
159  public function moveRowUp(int $index): void
160  {
161  $this->rows->moveCategoryUp($index);
162  }
163 
164  public function moveRowDown(int $index): void
165  {
166  $this->rows->moveCategoryDown($index);
167  }
168 
172  public function removeRows(array $array): void
173  {
174  $this->rows->removeCategories($array);
175  }
176 
177  public function removeRow(int $index): void
178  {
179  $this->rows->removeCategory($index);
180  }
181 
186  public function getBipolarAdjective(int $a_index): string
187  {
188  if ($a_index === 1) {
190  }
192  }
193 
194  public function setBipolarAdjective(
195  int $a_index,
196  string $a_value
197  ): void {
198  if ($a_index === 1) {
199  $this->bipolar_adjective2 = $a_value;
200  } else {
201  $this->bipolar_adjective1 = $a_value;
202  }
203  }
204 
208  public function addPhrase(int $phrase_id): void
209  {
211  $ilDB = $this->db;
212 
213  $result = $ilDB->queryF(
214  "SELECT svy_category.* FROM svy_category, svy_phrase_cat WHERE svy_phrase_cat.category_fi = svy_category.category_id AND svy_phrase_cat.phrase_fi = %s AND (svy_category.owner_fi = %s OR svy_category.owner_fi = %s) ORDER BY svy_phrase_cat.sequence",
215  array('integer', 'integer', 'integer'),
216  array($phrase_id, 0, $ilUser->getId())
217  );
218  while ($row = $ilDB->fetchAssoc($result)) {
219  $neutral = $row["neutral"];
220  if ((int) $row["defaultvalue"] === 1 && (int) $row["owner_fi"] === 0) {
221  $this->columns->addCategory($this->lng->txt($row["title"]), 0, $neutral);
222  } else {
223  $this->columns->addCategory($row["title"], 0, $neutral);
224  }
225  }
226  }
227 
231  public function getQuestionDataArray(int $id): array
232  {
233  $ilDB = $this->db;
234 
235  $result = $ilDB->queryF(
236  "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",
237  array('integer'),
238  array($id)
239  );
240  if ($result->numRows() === 1) {
241  return $ilDB->fetchAssoc($result);
242  }
243 
244  return array();
245  }
246 
247  public function loadFromDb(int $question_id): void
248  {
249  $ilDB = $this->db;
250  $result = $ilDB->queryF(
251  "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",
252  array('integer'),
253  array($question_id)
254  );
255  if ($result->numRows() === 1) {
256  $data = $ilDB->fetchAssoc($result);
257  $this->setId((int) $data["question_id"]);
258  $this->setTitle((string) $data["title"]);
259  $this->label = (string) $data['label'];
260  $this->setDescription((string) $data["description"]);
261  $this->setObjId((int) $data["obj_fi"]);
262  $this->setAuthor((string) $data["author"]);
263  $this->setOwner((int) $data["owner_fi"]);
264  $this->setQuestiontext(ilRTE::_replaceMediaObjectImageSrc((string) $data["questiontext"], 1));
265  $this->setObligatory((bool) $data["obligatory"]);
266  $this->setComplete((bool) $data["complete"]);
267  $this->setOriginalId((int) $data["original_id"]);
268  $this->setSubtype((int) $data["subtype"]);
269  $this->setRowSeparators((bool) $data["row_separators"]);
270  $this->setNeutralColumnSeparator((bool) $data["neutral_column_separator"]);
271  $this->setColumnSeparators((bool) $data["column_separators"]);
272  $this->setColumnPlaceholders((bool) $data["column_placeholders"]);
273  $this->setLegend((bool) $data["legend"]);
274  $this->setSingleLineRowCaption((string) $data["singleline_row_caption"]);
275  $this->setRepeatColumnHeader((bool) $data["repeat_column_header"]);
276  $this->setBipolarAdjective(0, (string) $data["bipolar_adjective1"]);
277  $this->setBipolarAdjective(1, (string) $data["bipolar_adjective2"]);
278  $this->setLayout($data["layout"]);
279  $this->flushColumns();
280 
281  $result = $ilDB->queryF(
282  "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",
283  array('integer'),
284  array($question_id)
285  );
286  if ($result->numRows() > 0) {
287  while ($data = $ilDB->fetchAssoc($result)) {
288  $this->columns->addCategory((string) $data["title"], (int) $data["other"], (int) $data["neutral"], null, ($data['scale']) ?: ($data['sequence'] + 1));
289  }
290  }
291 
292  $result = $ilDB->queryF(
293  "SELECT * FROM svy_qst_matrixrows WHERE question_fi = %s ORDER BY sequence",
294  array('integer'),
295  array($question_id)
296  );
297  while ($row = $ilDB->fetchAssoc($result)) {
298  $this->addRow((string) $row["title"], (string) $row['other'], (string) ($row['label'] ?? ""));
299  }
300  }
301  parent::loadFromDb($question_id);
302  }
303 
304  public function isComplete(): bool
305  {
306  return (
307  $this->getTitle() !== '' &&
308  $this->getAuthor() !== '' &&
309  $this->getQuestiontext() !== '' &&
310  $this->getColumnCount() &&
311  $this->getRowCount()
312  );
313  }
314 
315  public function saveToDb(int $original_id = 0): int
316  {
317  $ilDB = $this->db;
318 
319  $affectedRows = parent::saveToDb($original_id);
320 
321  if ($affectedRows === 1) {
322  $ilDB->manipulateF(
323  "DELETE FROM " . $this->getAdditionalTableName() . " WHERE question_fi = %s",
324  array('integer'),
325  array($this->getId())
326  );
327  $ilDB->manipulateF(
328  "INSERT INTO " . $this->getAdditionalTableName() . " (
329  question_fi, subtype, column_separators, row_separators, neutral_column_separator,column_placeholders,
330  legend, singleline_row_caption, repeat_column_header,
331  bipolar_adjective1, bipolar_adjective2, layout, tstamp)
332  VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
333  array(
334  'integer', 'integer', 'text', 'text', 'text', 'integer', 'text', 'text', 'text',
335  'text', 'text', 'text', 'integer'
336  ),
337  array(
338  $this->getId(),
339  $this->getSubtype(),
340  $this->getColumnSeparators(),
341  $this->getRowSeparators(),
342  $this->getNeutralColumnSeparator(),
343  $this->getColumnPlaceholders(),
344  $this->getLegend(),
345  $this->getSingleLineRowCaption(),
346  $this->getRepeatColumnHeader(),
347  $this->getBipolarAdjective(0),
348  $this->getBipolarAdjective(1),
349  serialize($this->getLayout()),
350  time()
351  )
352  );
353 
354  // saving material uris in the database
355  $this->saveMaterial();
356 
357  $this->saveColumnsToDb();
358  $this->saveRowsToDb();
359  }
360  return $affectedRows;
361  }
362 
363  public function saveBipolarAdjectives(
364  string $adjective1,
365  string $adjective2
366  ): void {
367  $ilDB = $this->db;
368 
369  $ilDB->manipulateF(
370  "UPDATE " . $this->getAdditionalTableName() . " SET bipolar_adjective1 = %s, bipolar_adjective2 = %s WHERE question_fi = %s",
371  array('text', 'text', 'integer'),
372  array(($adjective1 !== '') ? $adjective1 : null, ($adjective2 !== '') ? $adjective2 : null, $this->getId())
373  );
374  }
375 
376  public function saveColumnToDb(
377  string $columntext,
378  int $neutral = 0
379  ): int {
381  $ilDB = $this->db;
382 
383  $result = $ilDB->queryF(
384  "SELECT title, category_id FROM svy_category WHERE title = %s AND neutral = %s AND owner_fi = %s",
385  array('text', 'text', 'integer'),
386  array($columntext, $neutral, $ilUser->getId())
387  );
388  $insert = false;
389  $returnvalue = "";
390  $insert = true;
391  if ($result->numRows()) {
392  while ($row = $ilDB->fetchAssoc($result)) {
393  if (strcmp($row["title"] ?? '', $columntext) === 0) {
394  $returnvalue = $row["category_id"];
395  $insert = false;
396  }
397  }
398  }
399  if ($insert) {
400  $next_id = $ilDB->nextId('svy_category');
401  $affectedRows = $ilDB->manipulateF(
402  "INSERT INTO svy_category (category_id, title, defaultvalue, owner_fi, neutral, tstamp) VALUES (%s, %s, %s, %s, %s, %s)",
403  array('integer', 'text', 'text', 'integer', 'text', 'integer'),
404  array($next_id, $columntext, 0, $ilUser->getId(), $neutral, time())
405  );
406  $returnvalue = $next_id;
407  }
408  return $returnvalue;
409  }
410 
411  public function saveColumnsToDb(
412  int $original_id = 0
413  ): void {
414  $ilDB = $this->db;
415 
416  // save columns
417  $question_id = $this->getId();
418  if ($original_id > 0) {
419  $question_id = $original_id;
420  }
421 
422  // delete existing column relations
423  $affectedRows = $ilDB->manipulateF(
424  "DELETE FROM svy_variable WHERE question_fi = %s",
425  array('integer'),
426  array($question_id)
427  );
428  // create new column relations
429  for ($i = 0; $i < $this->getColumnCount(); $i++) {
430  $cat = $this->getColumn($i);
431  $column_id = $this->saveColumnToDb($cat->title, $cat->neutral);
432  $next_id = $ilDB->nextId('svy_variable');
433  $affectedRows = $ilDB->manipulateF(
434  "INSERT INTO svy_variable (variable_id, category_fi, question_fi, value1, other, sequence, scale, tstamp) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)",
435  array('integer','integer','integer','float','integer','integer', 'integer','integer'),
436  array($next_id, $column_id, $question_id, ($i + 1), $cat->other, $i, ($cat->scale > 0) ? $cat->scale : null, time())
437  );
438  }
440  }
441 
442  public function saveRowsToDb(
443  int $original_id = 0
444  ): void {
445  $ilDB = $this->db;
446 
447  // save rows
448  $question_id = $this->getId();
449  if ($original_id > 0) {
450  $question_id = $original_id;
451  }
452 
453  // delete existing rows
454  $affectedRows = $ilDB->manipulateF(
455  "DELETE FROM svy_qst_matrixrows WHERE question_fi = %s",
456  array('integer'),
457  array($question_id)
458  );
459  // create new rows
460  for ($i = 0; $i < $this->getRowCount(); $i++) {
461  $row = $this->getRow($i);
462  $next_id = $ilDB->nextId('svy_qst_matrixrows');
463  $affectedRows = $ilDB->manipulateF(
464  "INSERT INTO svy_qst_matrixrows (id_svy_qst_matrixrows, title, label, other, sequence, question_fi) VALUES (%s, %s, %s, %s, %s, %s)",
465  array('integer','text','text','integer','integer','integer'),
466  array($next_id, $row->title, $row->label, ($row->other) ? 1 : 0, $i, $question_id)
467  );
468  }
470  }
471 
478  public function toXML(
479  bool $a_include_header = true,
480  bool $obligatory_state = false
481  ): string {
482  $a_xml_writer = new ilXmlWriter();
483  $a_xml_writer->xmlHeader();
484  $this->insertXML($a_xml_writer, $a_include_header);
485  $xml = $a_xml_writer->xmlDumpMem(false);
486  if (!$a_include_header) {
487  $pos = strpos($xml, "?>");
488  $xml = substr($xml, $pos + 2);
489  }
490  return $xml;
491  }
492 
496  public function insertXML(
497  ilXmlWriter $a_xml_writer,
498  bool $a_include_header = true
499  ): void {
500  $attrs = array(
501  "id" => $this->getId(),
502  "title" => $this->getTitle(),
503  "type" => $this->getQuestionType(),
504  "subtype" => $this->getSubtype(),
505  "obligatory" => $this->getObligatory()
506  );
507  $a_xml_writer->xmlStartTag("question", $attrs);
508 
509  $a_xml_writer->xmlElement("description", null, $this->getDescription());
510  $a_xml_writer->xmlElement("author", null, $this->getAuthor());
511  $a_xml_writer->xmlStartTag("questiontext");
512  $this->addMaterialTag($a_xml_writer, $this->getQuestiontext());
513  $a_xml_writer->xmlEndTag("questiontext");
514 
515  $a_xml_writer->xmlStartTag("matrix");
516  $a_xml_writer->xmlStartTag("matrixrows");
517  for ($i = 0; $i < $this->getRowCount(); $i++) {
518  $attrs = array(
519  "id" => $i
520  );
521  if (strlen($this->getRow($i)->label)) {
522  $attrs['label'] = $this->getRow($i)->label;
523  }
524  if ($this->getRow($i)->other) {
525  $attrs['other'] = 1;
526  }
527  $a_xml_writer->xmlStartTag("matrixrow", $attrs);
528  $this->addMaterialTag($a_xml_writer, $this->getRow($i)->title);
529  $a_xml_writer->xmlEndTag("matrixrow");
530  }
531  $a_xml_writer->xmlEndTag("matrixrows");
532 
533  $a_xml_writer->xmlStartTag("responses");
534  if ($this->getBipolarAdjective(0) !== '' && ($this->getBipolarAdjective(1) !== '')) {
535  $a_xml_writer->xmlStartTag("bipolar_adjectives");
536  $attribs = array(
537  "label" => "0"
538  );
539  $a_xml_writer->xmlElement("adjective", $attribs, $this->getBipolarAdjective(0));
540  $attribs = array(
541  "label" => "1"
542  );
543  $a_xml_writer->xmlElement("adjective", $attribs, $this->getBipolarAdjective(1));
544  $a_xml_writer->xmlEndTag("bipolar_adjectives");
545  }
546  for ($i = 0; $i < $this->getColumnCount(); $i++) {
547  $attrs = array(
548  "id" => $i
549  );
550  if ($this->getColumn($i)->neutral) {
551  $attrs['label'] = 'neutral';
552  }
553  switch ($this->getSubtype()) {
554  case 0:
555  $a_xml_writer->xmlStartTag("response_single", $attrs);
556  break;
557  case 1:
558  $a_xml_writer->xmlStartTag("response_multiple", $attrs);
559  break;
560  }
561  $this->addMaterialTag($a_xml_writer, $this->getColumn($i)->title);
562  switch ($this->getSubtype()) {
563  case 0:
564  $a_xml_writer->xmlEndTag("response_single");
565  break;
566  case 1:
567  $a_xml_writer->xmlEndTag("response_multiple");
568  break;
569  }
570  }
571 
572  $a_xml_writer->xmlEndTag("responses");
573  $a_xml_writer->xmlEndTag("matrix");
574 
575  if (count($this->material)) {
576  if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $this->material["internal_link"], $matches)) {
577  $attrs = array(
578  "label" => $this->material["title"]
579  );
580  $a_xml_writer->xmlStartTag("material", $attrs);
581  $intlink = "il_" . IL_INST_ID . "_" . $matches[2] . "_" . $matches[3];
582  if (strcmp($matches[1], "") !== 0) {
583  $intlink = $this->material["internal_link"];
584  }
585  $a_xml_writer->xmlElement("mattext", null, $intlink);
586  $a_xml_writer->xmlEndTag("material");
587  }
588  }
589 
590  $a_xml_writer->xmlStartTag("metadata");
591  $a_xml_writer->xmlStartTag("metadatafield");
592  $a_xml_writer->xmlElement("fieldlabel", null, "column_separators");
593  $a_xml_writer->xmlElement("fieldentry", null, $this->getColumnSeparators());
594  $a_xml_writer->xmlEndTag("metadatafield");
595 
596  $a_xml_writer->xmlStartTag("metadatafield");
597  $a_xml_writer->xmlElement("fieldlabel", null, "row_separators");
598  $a_xml_writer->xmlElement("fieldentry", null, $this->getRowSeparators());
599  $a_xml_writer->xmlEndTag("metadatafield");
600 
601  $a_xml_writer->xmlStartTag("metadatafield");
602  $a_xml_writer->xmlElement("fieldlabel", null, "neutral_column_separator");
603  $a_xml_writer->xmlElement("fieldentry", null, $this->getNeutralColumnSeparator());
604  $a_xml_writer->xmlEndTag("metadatafield");
605 
606  $a_xml_writer->xmlStartTag("metadatafield");
607  $a_xml_writer->xmlElement("fieldlabel", null, "layout");
608  $a_xml_writer->xmlElement("fieldentry", null, serialize($this->getLayout()));
609  $a_xml_writer->xmlEndTag("metadatafield");
610 
611  $a_xml_writer->xmlEndTag("metadata");
612 
613  $a_xml_writer->xmlEndTag("question");
614  }
615 
616  public function syncWithOriginal(): void
617  {
618  if ($this->getOriginalId()) {
619  parent::syncWithOriginal();
620  $this->saveColumnsToDb($this->getOriginalId());
621  $this->saveRowsToDb($this->getOriginalId());
622  }
623  }
624 
628  public function addStandardNumbers(
629  int $lower_limit,
630  int $upper_limit
631  ): void {
632  for ($i = $lower_limit; $i <= $upper_limit; $i++) {
633  $this->columns->addCategory($i);
634  }
635  }
636 
641  public function savePhrase(
642  string $title
643  ): void {
645  $ilDB = $this->db;
646 
647  $next_id = $ilDB->nextId('svy_phrase');
648  $ilDB->manipulateF(
649  "INSERT INTO svy_phrase (phrase_id, title, defaultvalue, owner_fi, tstamp) VALUES (%s, %s, %s, %s, %s)",
650  array('integer','text','text','integer','integer'),
651  array($next_id, $title, 1, $ilUser->getId(), time())
652  );
653  $phrase_id = $next_id;
654 
655  $counter = 1;
656  $phrase_data = $this->edit_manager->getPhraseData();
657  foreach ($phrase_data as $data) {
658  $next_id = $ilDB->nextId('svy_category');
659  $affectedRows = $ilDB->manipulateF(
660  "INSERT INTO svy_category (category_id, title, defaultvalue, owner_fi, tstamp, neutral) VALUES (%s, %s, %s, %s, %s, %s)",
661  array('integer','text','text','integer','integer','text'),
662  array($next_id, $data['answer'], 1, $ilUser->getId(), time(), $data['neutral'])
663  );
664  $category_id = $next_id;
665  $next_id = $ilDB->nextId('svy_phrase_cat');
666  $affectedRows = $ilDB->manipulateF(
667  "INSERT INTO svy_phrase_cat (phrase_category_id, phrase_fi, category_fi, sequence, other, scale) VALUES (%s, %s, %s, %s, %s, %s)",
668  array('integer', 'integer', 'integer','integer', 'integer', 'integer'),
669  array($next_id, $phrase_id, $category_id, $counter, ($data['other']) ? 1 : 0, $data['scale'])
670  );
671  $counter++;
672  }
673  }
674 
675  public function getQuestionType(): string
676  {
677  return "SurveyMatrixQuestion";
678  }
679 
683  public function getAdditionalTableName(): string
684  {
685  return "svy_qst_matrix";
686  }
687 
688  public function getWorkingDataFromUserInput(array $post_data): array
689  {
690  $data = array();
691  foreach ($post_data as $key => $value) {
692  switch ($this->getSubtype()) {
693  case 1:
694  case 0:
695  if (preg_match("/matrix_" . $this->getId() . "_(\d+)/", $key, $matches)) {
696  if (is_array($value)) {
697  foreach ($value as $val) {
698  $data[] = array("value" => $val,
699  "rowvalue" => $matches[1],
700  "textanswer" => $post_data['matrix_other_' . $this->getId(
701  ) . '_' . $matches[1]] ?? ""
702  );
703  }
704  } else {
705  $data[] = array("value" => $value,
706  "rowvalue" => $matches[1],
707  "textanswer" => $post_data['matrix_other_' . $this->getId(
708  ) . '_' . $matches[1]] ?? ""
709  );
710  }
711  }
712  break;
713  }
714  }
715  return $data;
716  }
717 
723  public function checkUserInput(
724  array $post_data,
725  int $survey_id
726  ): string {
727  if (!$this->getObligatory()) {
728  return "";
729  }
730  switch ($this->getSubtype()) {
731  case 0:
732  $counter = 0;
733  foreach ($post_data as $key => $value) {
734  if (preg_match("/matrix_" . $this->getId() . "_(\d+)/", $key, $matches)) {
735  if (array_key_exists('matrix_other_' . $this->getId() . "_" . $matches[1], $post_data) && strlen($post_data['matrix_other_' . $this->getId() . "_" . $matches[1]]) == 0) {
736  return $this->lng->txt("question_mr_no_other_answer");
737  }
738  $counter++;
739  }
740  }
741  if ($counter !== $this->getRowCount()) {
742  return $this->lng->txt("matrix_question_radio_button_not_checked");
743  }
744  break;
745  case 1:
746  $counter = 0;
747  foreach ($post_data as $key => $value) {
748  if (preg_match("/matrix_" . $this->getId() . "_(\d+)/", $key, $matches)) {
749  if (array_key_exists('matrix_other_' . $this->getId() . "_" . $matches[1], $post_data) && strlen($post_data['matrix_other_' . $this->getId() . "_" . $matches[1]]) == 0) {
750  return $this->lng->txt("question_mr_no_other_answer");
751  }
752  $counter++;
753  if ((!is_array($value)) || (count($value) < 1)) {
754  return $this->lng->txt("matrix_question_checkbox_not_checked");
755  }
756  }
757  }
758  if ($counter !== $this->getRowCount()) {
759  return $this->lng->txt("matrix_question_checkbox_not_checked");
760  }
761  break;
762  }
763  return "";
764  }
765 
766  public function saveUserInput(
767  array $post_data,
768  int $active_id,
769  bool $a_return = false
770  ): ?array {
771  $ilDB = $this->db;
772 
773  $answer_data = array();
774 
775  // gather data
776  switch ($this->getSubtype()) {
777  case 0:
778  foreach ($post_data as $key => $value) {
779  if (preg_match("/matrix_" . $this->getId() . "_(\d+)/", $key, $matches)) {
780  if (strlen($value)) {
781  $other_value = (array_key_exists('matrix_other_' . $this->getId() . '_' . $matches[1], $post_data))
782  ? $this->stripSlashesAddSpaceFallback($post_data['matrix_other_' . $this->getId() . '_' . $matches[1]])
783  : null;
784  $answer_data[] = array("value" => $value,
785  "textanswer" => $other_value,
786  "rowvalue" => $matches[1]);
787  }
788  }
789  }
790  break;
791 
792  case 1:
793  foreach ($post_data as $key => $value) {
794  if (preg_match("/matrix_" . $this->getId() . "_(\d+)/", $key, $matches)) {
795  $other_value = (array_key_exists('matrix_other_' . $this->getId() . '_' . $matches[1], $post_data))
796  ? $this->stripSlashesAddSpaceFallback($post_data['matrix_other_' . $this->getId() . '_' . $matches[1]])
797  : null;
798  foreach ($value as $checked) {
799  $answer_data[] = array("value" => $checked,
800  "textanswer" => $other_value,
801  "rowvalue" => $matches[1]);
802  }
803  }
804  }
805  break;
806  }
807 
808  if ($a_return) {
809  return $answer_data;
810  }
811 
812  // #16387 - only if any input
813  if (count($answer_data)) {
814  // save data
815  foreach ($answer_data as $item) {
816  $next_id = $ilDB->nextId('svy_answer');
817  #20216
818  $fields = array();
819  $fields['answer_id'] = array("integer", $next_id);
820  $fields['question_fi'] = array("integer", $this->getId());
821  $fields['active_fi'] = array("integer", $active_id);
822  $fields['value'] = array("float", $item['value']);
823  $fields['textanswer'] = array("clob", $item['textanswer']);
824  $fields['rowvalue'] = array("integer", $item['rowvalue']);
825  $fields['tstamp'] = array("integer", time());
826 
827  $affectedRows = $ilDB->insert("svy_answer", $fields);
828  }
829  }
830  return null;
831  }
832 
836  public function deleteAdditionalTableData(
837  int $question_id
838  ): void {
839  parent::deleteAdditionalTableData($question_id);
840 
841  $ilDB = $this->db;
842  $ilDB->manipulateF(
843  "DELETE FROM svy_qst_matrixrows WHERE question_fi = %s",
844  array('integer'),
845  array($question_id)
846  );
847  }
848 
852  public function getSubtype(): ?int
853  {
854  return $this->subtype;
855  }
856 
860  public function setSubtype(int $a_subtype = 0): void
861  {
862  switch ($a_subtype) {
863  case 1:
864  case 2:
865  case 3:
866  case 4:
867  case 5:
868  case 6:
869  $this->subtype = $a_subtype;
870  break;
871  case 0:
872  default:
873  $this->subtype = 0;
874  break;
875  }
876  }
877 
881  public function setColumnSeparators(bool $enable = false): void
882  {
883  $this->columnSeparators = $enable;
884  }
885 
886  public function getColumnSeparators(): bool
887  {
889  }
890 
894  public function setRowSeparators(bool $enable = false): void
895  {
896  $this->rowSeparators = $enable;
897  }
898 
899  public function getRowSeparators(): bool
900  {
901  return $this->rowSeparators;
902  }
903 
904  public function setNeutralColumnSeparator(bool $enable = true): void
905  {
906  $this->neutralColumnSeparator = $enable;
907  }
908 
909  public function getNeutralColumnSeparator(): bool
910  {
912  }
913 
917  public function importAdditionalMetadata(array $a_meta): void
918  {
919  foreach ($a_meta as $key => $value) {
920  switch ($value["label"]) {
921  case "column_separators":
922  $this->setColumnSeparators($value["entry"]);
923  break;
924  case "row_separators":
925  $this->setRowSeparators($value["entry"]);
926  break;
927  case "layout":
928  $this->setLayout($value["entry"]);
929  break;
930  case "neutral_column_separator":
931  $this->setNeutralColumnSeparator($value["entry"]);
932  break;
933  }
934  }
935  }
936 
940  public function importAdjectives(array $a_data): void
941  {
942  $i = 0;
943  foreach ($a_data as $adjective) {
944  if (is_numeric($adjective["label"])) {
945  $this->setBipolarAdjective($adjective["label"], $adjective["text"]);
946  } else {
947  $this->setBipolarAdjective($i, $adjective["text"]);
948  }
949  $i++;
950  }
951  }
952 
956  public function importMatrix(
957  array $a_data
958  ): void {
959  foreach ($a_data as $row) {
960  $this->addRow($row['title'], $row['other'], $row['label']);
961  }
962  }
963 
967  public function importResponses(array $a_data): void
968  {
969  foreach ($a_data as $id => $data) {
970  $column = "";
971  foreach ($data["material"] as $material) {
972  $column .= $material["text"];
973  }
974  $this->columns->addCategory($column, 0, strcmp($data["label"], "neutral") == 0);
975  }
976  }
977 
981  public function usableForPrecondition(): bool
982  {
983  return false;
984  }
985 
989  public function getPreconditionValueOutput(string $value): string
990  {
991  return $value;
992  }
993 
997  public function getPreconditionSelectValue(
998  string $default,
999  string $title,
1000  string $variable
1001  ): ?ilFormPropertyGUI {
1002  $step3 = new ilSelectInputGUI($title, $variable);
1003  $options = $this->getPreconditionOptions();
1004  $step3->setOptions($options);
1005  $step3->setValue($default);
1006  return $step3;
1007  }
1008 
1018  public function saveLayout(
1019  float $percent_row,
1020  float $percent_columns,
1021  float $percent_bipolar_adjective1 = 0,
1022  float $percent_bipolar_adjective2 = 0,
1023  float $percent_neutral = 0
1024  ): void {
1025  $ilDB = $this->db;
1026 
1027  $layout = array(
1028  "percent_row" => $percent_row,
1029  "percent_columns" => $percent_columns,
1030  "percent_bipolar_adjective1" => $percent_bipolar_adjective1,
1031  "percent_bipolar_adjective2" => $percent_bipolar_adjective2,
1032  "percent_neutral" => $percent_neutral
1033  );
1034  $affectedRows = $ilDB->manipulateF(
1035  "UPDATE " . $this->getAdditionalTableName() . " SET layout = %s WHERE question_fi = %s",
1036  array('text', 'integer'),
1037  array(serialize($layout), $this->getId())
1038  );
1039  }
1040 
1041  public function getLayout(): array
1042  {
1043  if (count($this->layout) === 0) {
1044  if ($this->hasBipolarAdjectives() && $this->hasNeutralColumn()) {
1045  $this->layout = array(
1046  "percent_row" => 30,
1047  "percent_columns" => 40,
1048  "percent_bipolar_adjective1" => 10,
1049  "percent_bipolar_adjective2" => 10,
1050  "percent_neutral" => 10
1051  );
1052  } elseif ($this->hasBipolarAdjectives()) {
1053  $this->layout = array(
1054  "percent_row" => 30,
1055  "percent_columns" => 50,
1056  "percent_bipolar_adjective1" => 10,
1057  "percent_bipolar_adjective2" => 10,
1058  "percent_neutral" => 0
1059  );
1060  } elseif ($this->hasNeutralColumn()) {
1061  $this->layout = array(
1062  "percent_row" => 30,
1063  "percent_columns" => 50,
1064  "percent_bipolar_adjective1" => 0,
1065  "percent_bipolar_adjective2" => 0,
1066  "percent_neutral" => 20
1067  );
1068  } else {
1069  $this->layout = array(
1070  "percent_row" => 30,
1071  "percent_columns" => 70,
1072  "percent_bipolar_adjective1" => 0,
1073  "percent_bipolar_adjective2" => 0,
1074  "percent_neutral" => 0
1075  );
1076  }
1077  }
1078  return $this->layout;
1079  }
1080 
1084  public function setLayout($layout): void
1085  {
1086  if (is_array($layout)) {
1087  $this->layout = $layout;
1088  } else {
1089  $this->layout = unserialize($layout, ['allowed_classes' => false]) ?: [];
1090  }
1091  }
1092 
1096  public function hasBipolarAdjectives(): bool
1097  {
1098  return $this->getBipolarAdjective(0) !== '' && $this->getBipolarAdjective(1) !== '';
1099  }
1100 
1104  public function hasNeutralColumn(): bool
1105  {
1106  for ($i = 0; $i < $this->getColumnCount(); $i++) {
1107  $column = $this->getColumn($i);
1108  if ($column->neutral && strlen($column->title)) {
1109  return true;
1110  }
1111  }
1112  return false;
1113  }
1114 
1118  public function setColumnPlaceholders(bool $a_value = false): void
1119  {
1120  $this->columnPlaceholders = $a_value;
1121  }
1122 
1123  public function getColumnPlaceholders(): bool
1124  {
1126  }
1127 
1131  public function setLegend(bool $a_value = false): void
1132  {
1133  $this->legend = $a_value;
1134  }
1135 
1136  public function getLegend(): bool
1137  {
1138  return $this->legend;
1139  }
1140 
1141  public function setSingleLineRowCaption(bool $a_value = false): void
1142  {
1143  $this->singleLineRowCaption = $a_value;
1144  }
1145 
1146  public function getSingleLineRowCaption(): bool
1147  {
1149  }
1150 
1151  public function setRepeatColumnHeader(bool $a_value = false): void
1152  {
1153  $this->repeatColumnHeader = $a_value;
1154  }
1155 
1156  public function getRepeatColumnHeader(): bool
1157  {
1159  }
1160 
1161 
1162  public function getRows(): SurveyCategories
1163  {
1164  return $this->rows;
1165  }
1166 
1167  public static function getMaxSumScore(int $survey_id): int
1168  {
1169  global $DIC;
1170 
1171  // we need max scale values of matrix rows * number of rows (type 5)
1172  $db = $DIC->database();
1173 
1174  $set = $db->queryF(
1175  "SELECT MAX(scale) max_sum_score, q.question_id FROM svy_svy_qst sq " .
1176  "JOIN svy_question q ON (sq.question_fi = q.question_id) " .
1177  "JOIN svy_variable v ON (v.question_fi = q.question_id) " .
1178  "WHERE sq.survey_fi = %s AND q.questiontype_fi = %s " .
1179  "GROUP BY (q.question_id)",
1180  ["integer", "integer"],
1181  [$survey_id, 5]
1182  );
1183  $max_score = [];
1184  while ($rec = $db->fetchAssoc($set)) {
1185  $max_score[$rec["question_id"]] = $rec["max_sum_score"];
1186  }
1187 
1188  $set = $db->queryF(
1189  "SELECT COUNT(mr.id_svy_qst_matrixrows) cnt_rows, q.question_id FROM svy_svy_qst sq " .
1190  "JOIN svy_question q ON (sq.question_fi = q.question_id) " .
1191  "JOIN svy_qst_matrixrows mr ON (mr.question_fi = q.question_id) " .
1192  "WHERE sq.survey_fi = %s AND q.questiontype_fi = %s " .
1193  "GROUP BY (q.question_id)",
1194  ["integer", "integer"],
1195  [$survey_id, 5]
1196  );
1197  $cnt_rows = [];
1198  while ($rec = $db->fetchAssoc($set)) {
1199  $cnt_rows[$rec["question_id"]] = $rec["cnt_rows"];
1200  }
1201 
1202  $sum_sum_score = 0;
1203  foreach ($max_score as $qid => $s) {
1204  $sum_sum_score += $s * $cnt_rows[$qid];
1205  }
1206 
1207  return $sum_sum_score;
1208  }
1209 }
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.
setSingleLineRowCaption(bool $a_value=false)
addStandardNumbers(int $lower_limit, int $upper_limit)
Adds standard numbers as columns.
addRowAtPosition(string $a_text, string $a_other, int $a_position)
importAdjectives(array $a_data)
Import bipolar adjectives from the question import file.
const IL_INST_ID
Definition: constants.php:40
addPhrase(int $phrase_id)
Adds a phrase to the question.
static getMaxSumScore(int $survey_id)
setObligatory(bool $obligatory=true)
fetchAssoc(ilDBStatement $statement)
setOriginalId(?int $original_id)
getPreconditionValueOutput(string $value)
Returns the output for a precondition value.
saveColumnsToDb(int $original_id=0)
saveBipolarAdjectives(string $adjective1, string $adjective2)
hasBipolarAdjectives()
Returns TRUE if bipolar adjectives exist.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setComplete(bool $a_complete)
int $subtype
Matrix question subtype 0 = Single choice 1 = Multiple choice 2 = Text 3 = Integer 4 = Double 5 = Dat...
setBipolarAdjective(int $a_index, string $a_value)
stripSlashesAddSpaceFallback(string $a_str)
Strip slashes with add space fallback, see https://mantis.ilias.de/view.php?id=19727 and https://mant...
saveLayout(float $percent_row, float $percent_columns, float $percent_bipolar_adjective1=0, float $percent_bipolar_adjective2=0, float $percent_neutral=0)
Saves the layout of a matrix question.
$index
Definition: metadata.php:145
getPreconditionOptions()
Returns the options for preconditions.
setNeutralColumnSeparator(bool $enable=true)
setLegend(bool $a_value=false)
Set whether the legend should be shown or not.
xmlEndTag(string $tag)
Writes an endtag.
global $DIC
Definition: feed.php:28
if($format !==null) $name
Definition: metadata.php:247
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
hasNeutralColumn()
Returns TRUE if a neutral column exists.
getPreconditionSelectValue(string $default, string $title, string $variable)
Creates a form property for the precondition value.
setColumnPlaceholders(bool $a_value=false)
Set whether placeholders should be used for the column titles or not.
setAuthor(string $author="")
setColumnSeparators(bool $enable=false)
Enables/Disables separators for the matrix columns.
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.
usableForPrecondition()
Returns if the question is usable for preconditions.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
saveUserInput(array $post_data, int $active_id, bool $a_return=false)
string $key
Consumer key/client ID value.
Definition: System.php:193
importMatrix(array $a_data)
Import matrix rows from the question import file.
__construct(string $title="", string $description="", string $author="", string $questiontext="", int $owner=-1)
$xml
Definition: metadata.php:351
deleteAdditionalTableData(int $question_id)
Delete question data from additional table.
savePhrase(string $title)
Saves a set of columns to a default phrase (data currently comes from session)
setTitle(string $title="")
importAdditionalMetadata(array $a_meta)
Import additional meta data from the question import file.
getSubtype()
Returns the subtype of the matrix question.
setSubtype(int $a_subtype=0)
Sets the subtype of the matrix question.
getBipolarAdjective(int $a_index)
Returns one of the bipolar adjectives.
queryF(string $query, array $types, array $values)
checkUserInput(array $post_data, int $survey_id)
Checks the input of the active user for obligatory status and entered values.
addRow(string $a_text, string $a_other, string $a_label)
setRepeatColumnHeader(bool $a_value=false)
getQuestionDataArray(int $id)
Returns the question data fields from the database.
setRowSeparators(bool $enable=false)
Enables/Disables separators for the matrix rows.
setOwner(int $owner=0)
toXML(bool $a_include_header=true, bool $obligatory_state=false)
Returns an xml representation of the question.
This class represents a property in a property form.
__construct(Container $dic, ilPlugin $plugin)
$ilUser
Definition: imgupload.php:34
insertXML(ilXmlWriter $a_xml_writer, bool $a_include_header=true)
Adds the question XML to a given XMLWriter object.
xmlStartTag(string $tag, ?array $attrs=null, bool $empty=false, bool $encode=true, bool $escape=true)
Writes a starttag.
xmlElement(string $tag, $attrs=null, $data=null, $encode=true, $escape=true)
Writes a basic element (no children, just textual content)
setDescription(string $description="")
getAdditionalTableName()
Returns the name of the additional question data table in the database.
importResponses(array $a_data)
Import response data from the question import file.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setObjId(int $obj_id=0)
Set the reference(?) id of the container object.
saveColumnToDb(string $columntext, int $neutral=0)
getWorkingDataFromUserInput(array $post_data)
$i
Definition: metadata.php:41