ILIAS  trunk Revision v12.0_alpha-1540-g00f839d5fa1
class.ilAssQuestionList.php
Go to the documentation of this file.
1<?php
2
21use ILIAS\Notes\Service as NotesService;
22use ILIAS\Refinery\Factory as Refinery;
23
31{
32 private array $parentObjIdsFilter = [];
33 private ?int $parentObjId = null;
34 private string $parentObjType = 'qpl';
35 private array $availableTaxonomyIds = [];
36 private array $fieldFilters = [];
37 private array $taxFilters = [];
39 private array $taxParentIds = [];
40 private array $taxParentTypes = [];
41 private ?int $answerStatusActiveId = null;
42 protected bool $join_obj_data = true;
43
47 public const QUESTION_ANSWER_STATUS_NON_ANSWERED = 'nonAnswered';
48 public const QUESTION_ANSWER_STATUS_WRONG_ANSWERED = 'wrongAnswered';
49 public const QUESTION_ANSWER_STATUS_CORRECT_ANSWERED = 'correctAnswered';
50
54 public const ANSWER_STATUS_FILTER_ALL_NON_CORRECT = 'allNonCorrect';
55 public const ANSWER_STATUS_FILTER_NON_ANSWERED_ONLY = 'nonAnswered';
56 public const ANSWER_STATUS_FILTER_WRONG_ANSWERED_ONLY = 'wrongAnswered';
57
58 private string $answerStatusFilter = '';
59
60 public const QUESTION_INSTANCE_TYPE_ORIGINALS = 'QST_INSTANCE_TYPE_ORIGINALS';
61 public const QUESTION_INSTANCE_TYPE_DUPLICATES = 'QST_INSTANCE_TYPE_DUPLICATES';
62 public const QUESTION_INSTANCE_TYPE_ALL = 'QST_INSTANCE_TYPE_ALL';
64
65 private array $includeQuestionIdsFilter = [];
66 private array $excludeQuestionIdsFilter = [];
67
68 public const QUESTION_COMMENTED_ONLY = '1';
69 public const QUESTION_COMMENTED_EXCLUDED = '2';
70 protected ?string $filter_comments = null;
71
72 protected array $questions = [];
73
74 private ?string $order_field = null;
75 private ?string $order_direction = null;
76 private ?Range $range = null;
77
78 public function __construct(
79 private ilDBInterface $db,
80 private ilLanguage $lng,
81 private Refinery $refinery,
82 private ilComponentRepository $component_repository,
83 private ?NotesService $notes_service = null
84 ) {
85 }
86
87 public function setOrder(?Order $order = null): void
88 {
89 [
90 'order_field' => $this->order_field,
91 'order_direction' => $this->order_direction
92 ] = $this->getOrderFieldAndDirection($order);
93 }
94
95 public function setRange(?Range $range = null): void
96 {
97 $this->range = $range;
98 }
99
100 public function getParentObjId(): ?int
101 {
102 return $this->parentObjId;
103 }
104
105 public function setParentObjId(?int $parentObjId): void
106 {
107 $this->parentObjId = $parentObjId;
108 }
109
110 public function setParentObjectType(string $parentObjType): void
111 {
112 $this->parentObjType = $parentObjType;
113 }
114
115 public function setParentObjIdsFilter(array $parentObjIdsFilter): void
116 {
117 $this->parentObjIdsFilter = $parentObjIdsFilter;
118 }
119
121 {
122 $this->questionInstanceTypeFilter = (string) $questionInstanceTypeFilter;
123 }
124
125 public function setIncludeQuestionIdsFilter(array $questionIdsFilter): void
126 {
127 $this->includeQuestionIdsFilter = $questionIdsFilter;
128 }
129
131 {
132 $this->excludeQuestionIdsFilter = $excludeQuestionIdsFilter;
133 }
134
135 public function addFieldFilter(string $fieldName, mixed $fieldValue): void
136 {
137 $this->fieldFilters[$fieldName] = $fieldValue;
138 }
139
140 public function addTaxonomyFilter($taxId, $taxNodes, $parentObjId, $parentObjType): void
141 {
142 $this->taxFilters[$taxId] = $taxNodes;
143 $this->taxParentIds[$taxId] = $parentObjId;
144 $this->taxParentTypes[$taxId] = $parentObjType;
145 }
146
147 public function addTaxonomyFilterNoTaxonomySet(bool $flag): void
148 {
149 $this->taxFiltersExcludeAnyObjectsWithTaxonomies = $flag;
150 }
151
153 {
154 $this->availableTaxonomyIds = $availableTaxonomyIds;
155 }
156
158 {
159 $this->answerStatusActiveId = $answerStatusActiveId;
160 }
161
162 public function setAnswerStatusFilter(string $answerStatusFilter): void
163 {
164 $this->answerStatusFilter = $answerStatusFilter;
165 }
166
170 public function setJoinObjectData(bool $a_val): void
171 {
172 $this->join_obj_data = $a_val;
173 }
174
175 private function getParentObjFilterExpression(): ?string
176 {
177 if ($this->parentObjId) {
178 return "qpl_questions.obj_fi = {$this->db->quote($this->parentObjId, ilDBConstants::T_INTEGER)}";
179 }
180
181 if (!empty($this->parentObjIdsFilter)) {
182 return $this->db->in('qpl_questions.obj_fi', $this->parentObjIdsFilter, false, ilDBConstants::T_INTEGER);
183 }
184
185 return null;
186 }
187
188 private function getFieldFilterExpressions(): array
189 {
190 $expressions = [];
191
192 foreach ($this->fieldFilters as $fieldName => $fieldValue) {
193 switch ($fieldName) {
194 case 'title':
195 case 'description':
196 case 'author':
197 case 'lifecycle':
198 $expressions[] = $this->db->like("qpl_questions.$fieldName", ilDBConstants::T_TEXT, "%%$fieldValue%%");
199 break;
200 case 'type':
201 $expressions[] = "qpl_qst_type.type_tag = {$this->db->quote($fieldValue, ilDBConstants::T_TEXT)}";
202 break;
203 case 'question_id':
204 if ($fieldValue !== '' && !is_array($fieldValue)) {
205 $fieldValue = [$fieldValue];
206 }
207 $expressions[] = $this->db->in('qpl_questions.question_id', $fieldValue, false, ilDBConstants::T_INTEGER);
208 break;
209 case 'parent_title':
210 if ($this->join_obj_data) {
211 $expressions[] = $this->db->like('object_data.title', ilDBConstants::T_TEXT, "%%$fieldValue%%");
212 }
213 break;
214 }
215 }
216
217 return $expressions;
218 }
219
220 private function addFeedbackJoinIfNeeded(string $table_join): string
221 {
222 $feedback_join = match ($this->fieldFilters['feedback'] ?? null) {
223 'true' => 'INNER',
224 'false' => 'LEFT',
225 default => null
226 };
227
228 if (isset($feedback_join)) {
229 $sql = "{$feedback_join} JOIN qpl_fb_generic ON qpl_fb_generic.question_fi = qpl_questions.question_id ";
230 $table_join .= !str_contains($table_join, $sql) ? $sql : '';
231 }
232
233 return $table_join;
234 }
235
236 private function getTaxonomyFilterExpressions(): array
237 {
238 $expressions = $this->getFilterByAssignedTaxonomyIdsExpression();
239
240 $taxonomy_title = $this->fieldFilters['taxonomy_title'] ?? '';
241 $taxonomy_node_title = $this->fieldFilters['taxonomy_node_title'] ?? '';
242
243 if ($taxonomy_title === '' && $taxonomy_node_title === '') {
244 return $expressions;
245 }
246
247 $base = 'SELECT DISTINCT item_id FROM tax_node_assignment';
248
249 $like_taxonomy_title = $taxonomy_title !== ''
250 ? "AND {$this->db->like('object_data.title', ilDBConstants::T_TEXT, "%$taxonomy_title%", false)}"
251 : '';
252 $like_taxonomy_node_title = $taxonomy_node_title !== ''
253 ? "AND {$this->db->like('tax_node.title', ilDBConstants::T_TEXT, "%$taxonomy_node_title%", false)}"
254 : '';
255
256 $inner_join_object_data = "INNER JOIN object_data ON (object_data.obj_id = tax_node_assignment.tax_id AND object_data.type = 'tax' $like_taxonomy_title)";
257 $inner_join_tax_node = "INNER JOIN tax_node ON (tax_node.tax_id = tax_node_assignment.tax_id AND tax_node.type = 'taxn' AND tax_node_assignment.node_id = tax_node.obj_id $like_taxonomy_node_title)";
258
259 $expressions[] = "qpl_questions.question_id IN ($base $inner_join_object_data $inner_join_tax_node)";
260
261 return $expressions;
262 }
263
265 {
266 if ($this->taxFiltersExcludeAnyObjectsWithTaxonomies) {
267 return ['question_id NOT IN (SELECT DISTINCT item_id FROM tax_node_assignment)'];
268 }
269
270 $expressions = [];
271 foreach ($this->taxFilters as $tax_id => $tax_nodes) {
272 $question_ids = [];
273
274 if ($tax_nodes === []) {
275 continue;
276 }
277
278 foreach ($tax_nodes as $tax_node) {
279 $tax_items_by_tax_parent = $this->getTaxItems(
280 $this->taxParentTypes[$tax_id],
281 $this->taxParentIds[$tax_id],
282 $tax_id,
283 $tax_node
284 );
285
286 $tax_items_by_parent = $this->getTaxItems(
287 $this->parentObjType,
288 $this->parentObjId,
289 $tax_id,
290 $tax_node
291 );
292
293 $tax_items = array_merge($tax_items_by_tax_parent, $tax_items_by_parent);
294 foreach ($tax_items as $tax_item) {
295 $question_ids[$tax_item['item_id']] = $tax_item['item_id'];
296 }
297 }
298
299 $expressions[] = $this->db->in('question_id', $question_ids, false, ilDBConstants::T_INTEGER);
300 }
301
302 return $expressions;
303 }
304
305 protected function getTaxItems(string $parentType, int $parentObjId, int $taxId, int $taxNode): array
306 {
307 $taxTree = new ilTaxonomyTree($taxId);
308
309 $taxNodeAssignment = new ilTaxNodeAssignment(
310 $parentType,
312 'quest',
313 $taxId
314 );
315
316 $subNodes = $taxTree->getSubTreeIds($taxNode);
317 $subNodes[] = $taxNode;
318
319 return $taxNodeAssignment->getAssignmentsOfNode($subNodes);
320 }
321
322 private function getQuestionInstanceTypeFilterExpression(): ?string
323 {
324 return match ($this->questionInstanceTypeFilter) {
325 self::QUESTION_INSTANCE_TYPE_ORIGINALS => 'qpl_questions.original_id IS NULL',
326 self::QUESTION_INSTANCE_TYPE_DUPLICATES => 'qpl_questions.original_id IS NOT NULL',
327 default => null
328 };
329 }
330
331 private function getQuestionIdsFilterExpressions(): array
332 {
333 $expressions = [];
334
335 if (!empty($this->includeQuestionIdsFilter)) {
336 $expressions[] = $this->db->in(
337 'qpl_questions.question_id',
338 $this->includeQuestionIdsFilter,
339 false,
341 );
342 }
343
344 if (!empty($this->excludeQuestionIdsFilter)) {
345 $IN = $this->db->in(
346 'qpl_questions.question_id',
347 $this->excludeQuestionIdsFilter,
348 true,
350 );
351
352 $expressions[] = $IN === ' 1=2 ' ? ' 1=1 ' : $IN; // required for ILIAS < 5.0
353 }
354
355 return $expressions;
356 }
357
358 private function getAnswerStatusFilterExpressions(): array
359 {
360 return match ($this->answerStatusFilter) {
361 self::ANSWER_STATUS_FILTER_ALL_NON_CORRECT => ['
362 (tst_test_result.question_fi IS NULL OR tst_test_result.points < qpl_questions.points)
363 '],
364 self::ANSWER_STATUS_FILTER_NON_ANSWERED_ONLY => ['tst_test_result.question_fi IS NULL'],
365 self::ANSWER_STATUS_FILTER_WRONG_ANSWERED_ONLY => [
366 'tst_test_result.question_fi IS NOT NULL',
367 'tst_test_result.points < qpl_questions.points'
368 ],
369 default => [],
370 };
371 }
372
373 private function getTableJoinExpression(): string
374 {
375 $table_join = 'INNER JOIN qpl_qst_type ON qpl_qst_type.question_type_id = qpl_questions.question_type_fi ';
376
377 if ($this->join_obj_data) {
378 $table_join .= '
379 INNER JOIN object_data ON object_data.obj_id = qpl_questions.obj_fi
380 INNER JOIN object_reference ON object_reference.obj_id = object_data.obj_id
381 ';
382 }
383
384 if (
385 $this->parentObjType === 'tst'
386 && $this->questionInstanceTypeFilter === self::QUESTION_INSTANCE_TYPE_ALL
387 ) {
388 $table_join .= 'INNER JOIN tst_test_question tstquest ON tstquest.question_fi = qpl_questions.question_id';
389 }
390
391 $table_join = $this->addFeedbackJoinIfNeeded($table_join);
392
393 if ($this->answerStatusActiveId) {
394 $table_join .= "
395 LEFT JOIN tst_test_result
396 ON tst_test_result.question_fi = qpl_questions.question_id
397 AND tst_test_result.active_fi = {$this->db->quote($this->answerStatusActiveId, ilDBConstants::T_INTEGER)}
398 ";
399 }
400
401 return $table_join;
402 }
403
404 private function getConditionalFilterExpression(): string
405 {
406 $conditions = [];
407
408 if ($this->getQuestionInstanceTypeFilterExpression() !== null) {
409 $conditions[] = $this->getQuestionInstanceTypeFilterExpression();
410 }
411
412 if ($this->getParentObjFilterExpression() !== null) {
413 $conditions[] = $this->getParentObjFilterExpression();
414 }
415
416 $conditions = array_merge(
417 $conditions,
422 );
423
424 $conditions = implode(' AND ', $conditions);
425 return $conditions !== '' ? "AND $conditions" : '';
426 }
427
428 private function getSelectFieldsExpression(): string
429 {
430 $select_fields = [
431 'qpl_questions.*',
432 'qpl_qst_type.type_tag AS question_type',
433 'qpl_qst_type.plugin',
434 'qpl_qst_type.plugin_name',
435 'qpl_questions.points max_points'
436 ];
437
438 if ($this->join_obj_data) {
439 $select_fields[] = 'object_data.title parent_title, object_data.type parent_type, object_reference.ref_id parent_ref_id';
440 }
441
442 if ($this->answerStatusActiveId) {
443 $select_fields[] = 'tst_test_result.points reached_points';
444 $select_fields[] = "CASE
445 WHEN tst_test_result.points IS NULL THEN '" . self::QUESTION_ANSWER_STATUS_NON_ANSWERED . "'
446 WHEN tst_test_result.points < qpl_questions.points THEN '" . self::QUESTION_ANSWER_STATUS_WRONG_ANSWERED . "'
447 ELSE '" . self::QUESTION_ANSWER_STATUS_CORRECT_ANSWERED . "'
448 END question_answer_status
449 ";
450 }
451
452 $select_fields[] = $this->generateFeedbackSubquery();
453
454 $select_fields[] = $this->generateTaxonomySubquery();
455
456 $select_fields = implode(', ', $select_fields);
457 return "SELECT DISTINCT $select_fields";
458 }
459
460 private function generateFeedbackSubquery(): string
461 {
462 $cases = [];
463 $tables = ['qpl_fb_generic', 'qpl_fb_specific'];
464
465 foreach ($tables as $table) {
466 $subquery = "SELECT 1 FROM $table WHERE $table.question_fi = qpl_questions.question_id AND $table.feedback <> ''";
467 $cases[] = "WHEN EXISTS ($subquery) THEN TRUE";
468 }
469
470 $page_object_table = 'page_object';
471 foreach ($tables as $table) {
472 $subquery = sprintf(
473 "SELECT 1 FROM $table JOIN $page_object_table ON $page_object_table.page_id = $table.feedback_id WHERE $page_object_table.parent_type IN ('%s', '%s') AND $page_object_table.is_empty <> 1 AND $table.question_fi = qpl_questions.question_id",
476 );
477 $cases[] = "WHEN EXISTS ($subquery) THEN TRUE";
478 }
479
480 $feedback_case_subquery = implode(' ', $cases);
481 return "CASE $feedback_case_subquery ELSE FALSE END AS feedback";
482 }
483
484 private function generateTaxonomySubquery(): string
485 {
486 $tax_node_assignment_table = 'tax_node_assignment';
487 $tax_subquery = "SELECT 1 FROM $tax_node_assignment_table WHERE $tax_node_assignment_table.item_id = qpl_questions.question_id AND $tax_node_assignment_table.item_type = 'quest'";
488 return "CASE WHEN EXISTS ($tax_subquery) THEN TRUE ELSE FALSE END AS taxonomies";
489 }
490
491 private function buildBasicQuery(): string
492 {
493 return "{$this->getSelectFieldsExpression()} FROM qpl_questions {$this->getTableJoinExpression()} WHERE qpl_questions.tstamp > 0";
494 }
495
496 private function getHavingFilterExpression(): string
497 {
498 $expressions = [];
499
500 foreach ($this->fieldFilters as $fieldName => $fieldValue) {
501 if ($fieldName === 'feedback') {
502 $fieldValue = strtoupper($fieldValue);
503 if (in_array($fieldValue, ['TRUE', 'FALSE'], true)) {
504 $expressions[] = "feedback IS $fieldValue";
505 }
506 continue;
507
508 }
509 }
510
511 $having = implode(' AND ', $expressions);
512 return $having !== '' ? "HAVING $having" : '';
513 }
514
515 private function buildOrderQueryExpression(): string
516 {
517 return $this->order_field === null || $this->order_direction === null || $this->order_field === 'question_type'
518 ? ''
519 : " ORDER BY `$this->order_field` $this->order_direction";
520 }
521
522 private function buildLimitQueryExpression(): string
523 {
524 if ($this->order_field === 'question_type'
525 || $this->range === null) {
526 return '';
527 }
528
529 $limit = max($this->range->getLength(), 0);
530 $offset = max($this->range->getStart(), 0);
531
532 return " LIMIT {$limit} OFFSET {$offset}";
533 }
534
535 private function getOrderFieldAndDirection(?Order $order): ?array
536 {
537 if ($order === null) {
538 return ['order_field' => null, 'order_direction' => null];
539 }
540
542 '',
543 static fn(string $index, string $key, string $value): array => [
544 $key,
545 strtoupper($value)
546 ]
547 );
548
549 if (!in_array($order_direction, [Order::ASC, Order::DESC], true)) {
550 $order_direction = Order::ASC;
551 }
552
553 return ['order_field' => $order_field, 'order_direction' => $order_direction];
554 }
555
556 private function buildQuery(): string
557 {
558 return implode(PHP_EOL, array_filter([
559 $this->buildBasicQuery(),
564 ]));
565 }
566
567 public function load(): void
568 {
569 $this->checkFilters();
570
571 $tags_trafo = $this->refinery->encode()->htmlSpecialCharsAsEntities();
572
573 $res = $this->db->query($this->buildQuery());
574 while ($row_from_db = $this->db->fetchAssoc($res)) {
576
577 if (!$this->isActiveQuestionType($row)) {
578 continue;
579 }
580
581 $row['title'] = $tags_trafo->transform($row['title'] ?? '&nbsp;');
582 $row['description'] = $tags_trafo->transform($row['description'] ?? '');
583 $row['author'] = $tags_trafo->transform($row['author']);
584 $row['taxonomies'] = $this->loadTaxonomyAssignmentData($row['obj_fi'], $row['question_id']);
585 $row['question_type'] = $this->lng->txt($row['question_type']);
586 $row['feedback'] = $row['feedback'] === 1;
587 $row['comments'] = $this->getNumberOfCommentsForQuestion($row['question_id']);
588
589 if (
590 $this->filter_comments === self::QUESTION_COMMENTED_ONLY && $row['comments'] === 0
591 || $this->filter_comments === self::QUESTION_COMMENTED_EXCLUDED && $row['comments'] > 0
592 ) {
593 continue;
594 }
595
596 $this->questions[$row['question_id']] = $row;
597 }
598
599 if ($this->order_field === 'question_type') {
600 $this->questions = $this->orderAndLimitByQuestionType($this->questions);
601 }
602 }
603
605 array $questions
606 ): array {
607 $direction = $this->order_direction === Order::DESC ? -1 : 1;
608 usort(
610 fn(array $a, array $b): int => $direction * ($a['question_type'] <=> $b['question_type'])
611 );
612
613 if ($this->range !== null) {
614 $questions = array_slice(
616 $this->range->getStart(),
617 $this->range->getLength()
618 );
619 }
620
621 return $questions;
622 }
623
624 public function getTotalRowCount(
625 mixed $additional_viewcontrol_data,
626 mixed $filter_data,
627 mixed $additional_parameters
628 ): ?int {
629 $this->checkFilters();
630
631 $count = 'COUNT(*)';
632 $query = "SELECT $count FROM qpl_questions {$this->getTableJoinExpression()} WHERE qpl_questions.tstamp > 0 {$this->getConditionalFilterExpression()}";
633
634 return (int) ($this->db->query($query)->fetch()[$count] ?? 0);
635 }
636
637 protected function getNumberOfCommentsForQuestion(int $question_id): int
638 {
639 if ($this->notes_service === null) {
640 return 0;
641 }
642 $notes_context = $this->notes_service->data()->context(
643 $this->getParentObjId(),
644 $question_id,
645 'quest'
646 );
647 return $this->notes_service->domain()->getNrOfCommentsForContext($notes_context);
648 }
649
650 public function setCommentFilter(?int $commented = null): void
651 {
652 $this->filter_comments = $commented;
653 }
654
656 int $parent_obj_id,
657 int $question_id
658 ): array {
659 $tax_assignment_data = [];
660 foreach ($this->availableTaxonomyIds as $tax_id) {
661 $tax_tree = new ilTaxonomyTree($tax_id);
662
663 $tax_assignment = new ilTaxNodeAssignment('qpl', $parent_obj_id, 'quest', $tax_id);
664 $assignments = $tax_assignment->getAssignmentsOfItem($question_id);
665
666 foreach ($assignments as $ass_data) {
667 if (!isset($tax_assignment_data[$ass_data['tax_id']])) {
668 $tax_assignment_data[$ass_data['tax_id']] = [];
669 }
670
671 $ass_data['node_lft'] = $tax_tree->getNodeData($ass_data['node_id']);
672
673 $tax_assignment_data[$ass_data['tax_id']][$ass_data['node_id']] = $ass_data;
674 }
675 }
676
677 return $tax_assignment_data;
678 }
679
680 private function isActiveQuestionType(array $questionData): bool
681 {
682 if (!isset($questionData['plugin'])) {
683 return false;
684 }
685
686 if (!$questionData['plugin']) {
687 return true;
688 }
689
690 if (
691 !isset($questionData['plugin_name'])
692 || !$this->component_repository->getComponentByTypeAndName(
694 'TestQuestionPool'
695 )->getPluginSlotById('qst')->hasPluginName($questionData['plugin_name'])
696 ) {
697 return false;
698 }
699
700 return $this->component_repository
701 ->getComponentByTypeAndName(ilComponentInfo::TYPE_COMPONENT, 'TestQuestionPool')
702 ->getPluginSlotById('qst')
703 ->getPluginByName($questionData['plugin_name'])
704 ->isActive();
705 }
706
707 public function getDataArrayForQuestionId(int $questionId): array
708 {
709 return $this->questions[$questionId];
710 }
711
712 public function getQuestionDataArray(): array
713 {
714 return $this->questions;
715 }
716
717 public function isInList(int $questionId): bool
718 {
719 return isset($this->questions[$questionId]);
720 }
721
731 public function getTitle(string $a_comp_id, string $a_item_type, int $a_item_id): string
732 {
733 if ($a_comp_id !== 'qpl' || $a_item_type !== 'quest' || !$a_item_id) {
734 return '';
735 }
736
737 return $this->questions[$a_item_id]['title'] ?? '';
738 }
739
740 private function checkFilters(): void
741 {
742 if ($this->answerStatusFilter !== '' && !$this->answerStatusActiveId) {
744 'No active id given! You cannot use the answer status filter without giving an active id.'
745 );
746 }
747 }
748}
Builds data types.
Definition: Factory.php:36
Both the subject and the direction need to be specified when expressing an order.
Definition: Order.php:29
join($init, callable $fn)
Definition: Order.php:75
A simple class to express a naive range of whole positive numbers.
Definition: Range.php:29
const PAGE_OBJECT_TYPE_GENERIC_FEEDBACK
type for generic feedback page objects
const PAGE_OBJECT_TYPE_SPECIFIC_FEEDBACK
type for specific feedback page objects
setQuestionInstanceTypeFilter(?string $questionInstanceTypeFilter)
isActiveQuestionType(array $questionData)
setParentObjId(?int $parentObjId)
const ANSWER_STATUS_FILTER_ALL_NON_CORRECT
answer status filter value domain
setCommentFilter(?int $commented=null)
getTotalRowCount(mixed $additional_viewcontrol_data, mixed $filter_data, mixed $additional_parameters)
setParentObjIdsFilter(array $parentObjIdsFilter)
getDataArrayForQuestionId(int $questionId)
addFeedbackJoinIfNeeded(string $table_join)
getNumberOfCommentsForQuestion(int $question_id)
setOrder(?Order $order=null)
loadTaxonomyAssignmentData(int $parent_obj_id, int $question_id)
setIncludeQuestionIdsFilter(array $questionIdsFilter)
setAvailableTaxonomyIds(array $availableTaxonomyIds)
addTaxonomyFilter($taxId, $taxNodes, $parentObjId, $parentObjType)
setJoinObjectData(bool $a_val)
Set if object data table should be joined.
__construct(private ilDBInterface $db, private ilLanguage $lng, private Refinery $refinery, private ilComponentRepository $component_repository, private ?NotesService $notes_service=null)
setExcludeQuestionIdsFilter(array $excludeQuestionIdsFilter)
addFieldFilter(string $fieldName, mixed $fieldValue)
getTaxItems(string $parentType, int $parentObjId, int $taxId, int $taxNode)
getOrderFieldAndDirection(?Order $order)
setAnswerStatusActiveId(?int $answerStatusActiveId)
getTitle(string $a_comp_id, string $a_item_type, int $a_item_id)
Get title of an assigned item.
setRange(?Range $range=null)
orderAndLimitByQuestionType(array $questions)
setParentObjectType(string $parentObjType)
setAnswerStatusFilter(string $answerStatusFilter)
addTaxonomyFilterNoTaxonomySet(bool $flag)
const QUESTION_ANSWER_STATUS_NON_ANSWERED
answer status domain for single questions
static completeMissingPluginName(array $question_type_data)
language handling
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...
Readable part of repository interface to ilComponentDataDB.
Interface ilDBInterface.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$res
Definition: ltiservices.php:69
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
global $lng
Definition: privfeed.php:26