ILIAS  release_5-2 Revision v5.2.25-18-g3f80b828510
class.ilAssOrderingElementList.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4require_once 'Modules/TestQuestionPool/classes/questions/class.ilAssOrderingElement.php';
5
13{
16
20
24
28
29 const IDENTIFIER_TYPE_SOLUTION = 'SolutionIds';
30 const IDENTIFIER_TYPE_RANDOM = 'RandomIds';
31
35 protected static $identifierRegistry = array(
36 self::IDENTIFIER_TYPE_SOLUTION => array(),
37 self::IDENTIFIER_TYPE_RANDOM => array()
38 );
39
43 protected $questionId;
44
48 protected $elements;
49
53 public function __construct()
54 {
55 $this->objectInstanceId = ++self::$objectInstanceCounter;
56
57 $this->questionId = null;
58 $this->resetElements();
59 }
60
64 public function __clone()
65 {
66 $this->objectInstanceId = ++self::$objectInstanceCounter;
67
68 $elements = array();
69
70 foreach($this as $key => $element)
71 {
72 $elements[$key] = clone $element;
73 }
74
75 $this->elements = $elements;
76 }
77
81 public function getClone()
82 {
83 $that = clone $this;
84 return $that;
85 }
86
90 public function getQuestionId()
91 {
92 return $this->questionId;
93 }
94
98 public function setQuestionId($questionId)
99 {
100 $this->questionId = $questionId;
101 }
102
106 public function loadFromDb()
107 {
108 $ilDB = isset($GLOBALS['DIC']) ? $GLOBALS['DIC']['ilDB'] : $GLOBALS['ilDB'];
109
110 $result = $ilDB->queryF(
111 "SELECT * FROM qpl_a_ordering WHERE question_fi = %s ORDER BY position ASC",
112 array('integer'), array($this->getQuestionId())
113 );
114
115 while( $row = $ilDB->fetchAssoc($result) )
116 {
117 $element = new ilAssOrderingElement();
118
119 $element->setRandomIdentifier($row['random_id']);
120 $element->setSolutionIdentifier($row['solution_key']);
121
122 $element->setPosition($row['position']);
123 $element->setIndentation($row["depth"]);
124
125 $element->setContent($row['answertext']);
126
127 $this->addElement($element);
128 $this->registerIdentifiers($element);
129 }
130 }
131
135 public function saveToDb()
136 {
138 $ilDB = isset($GLOBALS['DIC']) ? $GLOBALS['DIC']['ilDB'] : $GLOBALS['DIC']['ilDB'];
139
140 $ilDB->manipulateF(
141 "DELETE FROM qpl_a_ordering WHERE question_fi = %s",
142 array( 'integer' ), array( $this->getQuestionId() )
143 );
144
145 foreach($this as $orderElement)
146 {
147 $this->ensureValidIdentifiers($orderElement);
148
149 $ilDB->insert('qpl_a_ordering', array(
150 'answer_id' => array( 'integer', $ilDB->nextId('qpl_a_ordering') ),
151 'question_fi' => array( 'integer', $this->getQuestionId() ),
152 'answertext' => array( 'text', $orderElement->getContent()),
153 'solution_key' => array( 'integer', $orderElement->getSolutionIdentifier() ),
154 'random_id' => array( 'integer', $orderElement->getRandomIdentifier() ),
155 'position' => array( 'integer', $orderElement->getPosition() ),
156 'depth' => array( 'integer', $orderElement->getIndentation() ),
157 'tstamp' => array( 'integer', time() )
158 ));
159 }
160 }
161
165 public function clearElementContents()
166 {
167 foreach($this as $orderingElement)
168 {
169 $orderingElement->setContent('');
170 }
171 }
172
173 public function countElements()
174 {
175 return count($this->elements);
176 }
177
178 public function isFirstElementPosition($position)
179 {
180 return $position == 0;
181 }
182
183 public function isLastElementPosition($position)
184 {
185 return $position == ($this->countElements() - 1);
186 }
187
188 public function moveElementByPositions($currentPosition, $targetPosition)
189 {
190 $movingElement = $this->getElementByPosition($currentPosition);
191 $dodgingElement = $this->getElementByPosition($targetPosition);
192
193 $elementList = new self();
194 $elementList->setQuestionId($this->getQuestionId());
195
196 foreach($this as $element)
197 {
198 if( $element->getPosition() == $currentPosition )
199 {
200 $elementList->addElement($dodgingElement);
201 continue;
202 }
203
204 if( $element->getPosition() == $targetPosition )
205 {
206 $elementList->addElement($movingElement);
207 continue;
208 }
209
210 $elementList->addElement($element);
211 }
212
213 $dodgingElement->setPosition($currentPosition);
214 $movingElement->setPosition($targetPosition);
215
216 $this->setElements( $elementList->getElements() );
217 }
218
219 public function removeElement(ilAssOrderingElement $removeElement)
220 {
221 $elementList = new self();
222 $elementList->setQuestionId($this->getQuestionId());
223
224 $positionCounter = 0;
225
226 foreach($this as $element)
227 {
228 if( $element->isSameElement($removeElement) )
229 {
230 continue;
231 }
232
233 $element->setPosition($positionCounter++);
234 $elementList->addElement($element);
235 }
236 }
237
241 public function resetElements()
242 {
243 $this->elements = array();
244 }
245
249 public function setElements($elements)
250 {
251 $this->resetElements();
252
253 foreach($elements as $element)
254 {
255 $this->addElement($element);
256 }
257 }
258
262 public function getElements()
263 {
264 return $this->elements;
265 }
266
271 {
272 return $this->getIndexedElements(self::IDENTIFIER_TYPE_RANDOM);
273 }
274
278 public function getRandomIdentifierIndex()
279 {
280 return array_keys($this->getRandomIdentifierIndexedElements());
281 }
282
287 {
288 return $this->getIndexedElements(self::IDENTIFIER_TYPE_SOLUTION);
289 }
290
295 {
296 return array_keys($this->getSolutionIdentifierIndexedElements());
297 }
298
302 protected function getIndexedElements($identifierType)
303 {
304 $elements = array();
305
306 foreach($this as $element)
307 {
308 $elements[$this->fetchIdentifier($element, $identifierType)] = $element;
309 }
310
311 return $elements;
312 }
313
317 public function addElement(ilAssOrderingElement $element)
318 {
319 if( $this->hasValidIdentifiers($element) )
320 {
321 $this->registerIdentifiers($element);
322 }
323
324 $this->elements[] = $element;
325 }
326
331 public function getElementByPosition($position)
332 {
333 if( isset($this->elements[$position]) )
334 {
335 return $this->elements[$position];
336 }
337
338 return null;
339 }
340
345 public function elementExistByPosition($position)
346 {
347 return ( $this->getElementByPosition($position) !== null );
348 }
349
354 public function getElementByRandomIdentifier($randomIdentifier)
355 {
356 foreach($this as $element)
357 {
358 if( $element->getRandomIdentifier() != $randomIdentifier )
359 {
360 continue;
361 }
362
363 return $element;
364 }
365
366 return null;
367 }
368
373 public function elementExistByRandomIdentifier($randomIdentifier)
374 {
375 return ( $this->getElementByRandomIdentifier($randomIdentifier) !== null );
376 }
377
382 public function getElementBySolutionIdentifier($solutionIdentifier)
383 {
384 foreach($this as $element)
385 {
386 if( $element->getSolutionIdentifier() != $solutionIdentifier )
387 {
388 continue;
389 }
390
391 return $element;
392 }
393 return null;
394 }
395
400 public function elementExistBySolutionIdentifier($solutionIdentifier)
401 {
402 return ( $this->getElementBySolutionIdentifier($solutionIdentifier) !== null );
403 }
404
409 {
410 return $this->getRegisteredIdentifiers(self::IDENTIFIER_TYPE_SOLUTION);
411 }
412
417 {
418 return $this->getRegisteredIdentifiers(self::IDENTIFIER_TYPE_RANDOM);
419 }
420
425 protected function getRegisteredIdentifiers($identifierType)
426 {
427 if( !isset(self::$identifierRegistry[$identifierType][$this->getQuestionId()]) )
428 {
429 return array();
430 }
431
432 return self::$identifierRegistry[$identifierType][$this->getQuestionId()];
433 }
434
439 protected function hasValidIdentifiers(ilAssOrderingElement $element)
440 {
441 $identifier = $this->fetchIdentifier($element, self::IDENTIFIER_TYPE_SOLUTION);
442
443 if( !$this->isValidIdentifier(self::IDENTIFIER_TYPE_SOLUTION, $identifier) )
444 {
445 return false;
446 }
447
448 $identifier = $this->fetchIdentifier($element, self::IDENTIFIER_TYPE_RANDOM);
449
450 if( !$this->isValidIdentifier(self::IDENTIFIER_TYPE_RANDOM, $identifier) )
451 {
452 return false;
453 }
454
455 return true;
456 }
457
461 protected function ensureValidIdentifiers(ilAssOrderingElement $element)
462 {
463 $this->ensureValidIdentifier($element, self::IDENTIFIER_TYPE_SOLUTION);
464 $this->ensureValidIdentifier($element, self::IDENTIFIER_TYPE_RANDOM);
465 }
466
471 protected function ensureValidIdentifier(ilAssOrderingElement $element, $identifierType)
472 {
473 $identifier = $this->fetchIdentifier($element, $identifierType);
474
475 if( !$this->isValidIdentifier($identifierType, $identifier) )
476 {
477 $identifier = $this->buildIdentifier($identifierType);
478 $this->populateIdentifier($element, $identifierType, $identifier);
479 $this->registerIdentifier($element, $identifierType);
480 }
481 }
482
486 protected function registerIdentifiers(ilAssOrderingElement $element)
487 {
488 $this->registerIdentifier($element, self::IDENTIFIER_TYPE_SOLUTION);
489 $this->registerIdentifier($element, self::IDENTIFIER_TYPE_RANDOM);
490 }
491
497 protected function registerIdentifier(ilAssOrderingElement $element, $identifierType)
498 {
499 if( !isset(self::$identifierRegistry[$identifierType][$this->getQuestionId()]) )
500 {
501 self::$identifierRegistry[$identifierType][$this->getQuestionId()] = array();
502 }
503
504 $identifier = $this->fetchIdentifier($element, $identifierType);
505
506 if( !in_array($identifier, self::$identifierRegistry[$identifierType][$this->getQuestionId()]) )
507 {
508 self::$identifierRegistry[$identifierType][$this->getQuestionId()][] = $identifier;
509 }
510 }
511
518 protected function isIdentifierRegistered(ilAssOrderingElement $element, $identifierType)
519 {
520 if( !isset(self::$identifierRegistry[$identifierType][$this->getQuestionId()]) )
521 {
522 return false;
523 }
524
525 $identifier = $this->fetchIdentifier($element, $identifierType);
526
527 if( !in_array($identifier, self::$identifierRegistry[$identifierType][$this->getQuestionId()]) )
528 {
529 return false;
530 }
531
532 return true;
533 }
534
541 protected function fetchIdentifier(ilAssOrderingElement $element, $identifierType)
542 {
543 switch($identifierType)
544 {
546 case self::IDENTIFIER_TYPE_RANDOM: return $element->getRandomIdentifier();
547 }
548
549 $this->throwUnknownIdentifierTypeException($identifierType);
550 }
551
558 protected function populateIdentifier(ilAssOrderingElement $element, $identifierType, $identifier)
559 {
560 switch($identifierType)
561 {
562 case self::IDENTIFIER_TYPE_SOLUTION: $element->setSolutionIdentifier($identifier); break;
563 case self::IDENTIFIER_TYPE_RANDOM: $element->setRandomIdentifier($identifier); break;
564 default: $this->throwUnknownIdentifierTypeException($identifierType);
565 }
566 }
567
574 protected function isValidIdentifier($identifierType, $identifier)
575 {
576 switch($identifierType)
577 {
579 return self::isValidSolutionIdentifier($identifier);
580
582 return self::isValidRandomIdentifier($identifier);
583 }
584
585 $this->throwUnknownIdentifierTypeException($identifierType);
586 }
587
593 protected function buildIdentifier($identifierType)
594 {
595 switch($identifierType)
596 {
599 }
600
601 $this->throwUnknownIdentifierTypeException($identifierType);
602 }
603
608 protected function throwUnknownIdentifierTypeException($identifierType)
609 {
611 "unknown identifier type given (type: $identifierType)"
612 );
613 }
614
620 {
622 "could not build random identifier (max tries: $maxTries)"
623 );
624 }
625
630 protected function throwMissingReorderPositionException($randomIdentifier)
631 {
633 "cannot reorder element due to missing position (random identifier: $randomIdentifier)"
634 );
635 }
636
641 protected function throwUnknownRandomIdentifiersException($randomIdentifiers)
642 {
644 'cannot reorder element due to one or more unknown random identifiers '.
645 '(' . implode(', ', $randomIdentifiers) . ')'
646 );
647 }
648
652 protected function getLastSolutionIdentifier()
653 {
654 $lastSolutionIdentifier = null;
655
656 foreach( $this->getRegisteredSolutionIdentifiers() as $registeredIdentifier)
657 {
658 if( $lastSolutionIdentifier > $registeredIdentifier )
659 {
660 continue;
661 }
662
663 $lastSolutionIdentifier = $registeredIdentifier;
664 }
665
666 return $lastSolutionIdentifier;
667 }
668
672 protected function buildSolutionIdentifier()
673 {
674 $lastSolutionIdentifier = $this->getLastSolutionIdentifier();
675
676 if( $lastSolutionIdentifier === null )
677 {
678 return 0;
679 }
680
681 $nextSolutionIdentifier = $lastSolutionIdentifier + self::SOLUTION_IDENTIFIER_VALUE_INTERVAL;
682
683 return $nextSolutionIdentifier;
684 }
685
690 protected function buildRandomIdentifier()
691 {
692 $usedTriesCounter = 0;
693
694 do
695 {
696 if( $usedTriesCounter >= self::RANDOM_IDENTIFIER_BUILD_MAX_TRIES )
697 {
698 $this->throwCouldNotBuildRandomIdentifierException(self::RANDOM_IDENTIFIER_BUILD_MAX_TRIES);
699 }
700
701 $usedTriesCounter++;
702
705 $randomIdentifier = mt_rand($lowerBound, $upperBound);
706
707 $testElement = new ilAssOrderingElement();
708 $testElement->setRandomIdentifier($randomIdentifier);
709 }
710 while( $this->isIdentifierRegistered($testElement, self::IDENTIFIER_TYPE_RANDOM) );
711
712 return $randomIdentifier;
713 }
714
715 public static function isValidSolutionIdentifier($identifier)
716 {
717 if( !is_numeric($identifier) )
718 {
719 return false;
720 }
721
722 if( $identifier != (int)$identifier )
723 {
724 return false;
725 }
726
727 if( $identifier < 0 )
728 {
729 return false;
730 }
731
732 return true;
733 }
734
735 public static function isValidRandomIdentifier($identifier)
736 {
737 if( !is_numeric($identifier) )
738 {
739 return false;
740 }
741
742 if( $identifier != (int)$identifier )
743 {
744 return false;
745 }
746
747 if( $identifier < self::RANDOM_IDENTIFIER_RANGE_LOWER_BOUND )
748 {
749 return false;
750 }
751
752 if( $identifier > self::RANDOM_IDENTIFIER_RANGE_UPPER_BOUND )
753 {
754 return false;
755 }
756
757 return true;
758 }
759
760 public static function isValidPosition($position)
761 {
762 return self::isValidSolutionIdentifier($position); // this was the position earlier
763 }
764
765 public static function isValidIndentation($indentation)
766 {
767 return self::isValidPosition($indentation); // horizontal position ^^
768 }
769
774 {
775 foreach($this as $element)
776 {
777 $element->setRandomIdentifier( $this->buildRandomIdentifier() );
778 }
779 }
780
785 public function hasSameElementSetByRandomIdentifiers(self $otherList)
786 {
787 $numIntersectingElements = count(array_intersect(
788 $otherList->getRandomIdentifierIndex(), $this->getRandomIdentifierIndex()
789 ));
790
791 if( $numIntersectingElements != $this->countElements() )
792 {
793 return false;
794 }
795
796 if( $numIntersectingElements != $otherList->countElements() )
797 {
798 return false;
799 }
800
801 return true; // faster ;-)
802
803 $otherListRandomIdentifierIndex = $otherList->getRandomIdentifierIndex();
804
805 foreach($this as $orderingElement)
806 {
807 if( !in_array($orderingElement->getRandomIdentifier(), $otherListRandomIdentifierIndex) )
808 {
809 return false;
810 }
811
812 $randomIdentifierIndexMatchingsCount = count( array_keys(
813 $otherListRandomIdentifierIndex, $orderingElement->getRandomIdentifier(), false
814 ));
815
816 if( $randomIdentifierIndexMatchingsCount != 1 )
817 {
818 return false;
819 }
820 }
821
822 return $this->countElements() == $otherList->countElements();
823 }
824
825 public function getParityTrueElementList(self $otherList)
826 {
827 if( !$this->hasSameElementSetByRandomIdentifiers($otherList) )
828 {
829 throw new ilTestQuestionPoolException('cannot compare lists having different element sets');
830 }
831
832 $parityTrueElementList = new self();
833 $parityTrueElementList->setQuestionId($this->getQuestionId());
834
835 foreach($this as $thisElement)
836 {
837 $otherElement = $otherList->getElementByRandomIdentifier(
838 $thisElement->getRandomIdentifier()
839 );
840
841 if( $otherElement->getPosition() != $thisElement->getPosition() )
842 {
843 continue;
844 }
845
846 if( $otherElement->getIndentation() != $thisElement->getIndentation() )
847 {
848 continue;
849 }
850
851 $parityTrueElementList->addElement($thisElement);
852 }
853
854 return $parityTrueElementList;
855 }
856
862 public function reorderByRandomIdentifiers($randomIdentifiers)
863 {
864 $positionsMap = array_flip( array_values($randomIdentifiers) );
865
866 $orderedElements = array();
867
868 foreach($this as $element)
869 {
870 if( !isset($positionsMap[$element->getRandomIdentifier()]) )
871 {
872 $this->throwMissingReorderPositionException($element->getRandomIdentifier());
873 }
874
875 $position = $positionsMap[$element->getRandomIdentifier()];
876 unset($positionsMap[$element->getRandomIdentifier()]);
877
878 $element->setPosition($position);
879 $orderedElements[$position] = $element;
880 }
881
882 if( count($positionsMap) )
883 {
884 $this->throwUnknownRandomIdentifiersException( array_keys($positionsMap) );
885 }
886
887 ksort($orderedElements);
888
889 $this->setElements( array_values($orderedElements) );
890 }
891
896 {
897 foreach($this as $element)
898 {
899 $element->setIndentation(0);
900 }
901 }
902
907 public function getDifferenceElementList(self $otherElementList)
908 {
909 $differenceRandomIdentifierIndex = $this->getDifferenceRandomIdentifierIndex($otherElementList);
910
911 $differenceElementList = new self();
912 $differenceElementList->setQuestionId($this->getQuestionId());
913
914 foreach($differenceRandomIdentifierIndex as $randomIdentifier)
915 {
916 $element = $this->getElementByRandomIdentifier($randomIdentifier);
917 $differenceElementList->addElement($element);
918 }
919
920 return $differenceElementList;
921 }
922
927 protected function getDifferenceRandomIdentifierIndex(self $otherElementList)
928 {
929 $differenceRandomIdentifierIndex = array_diff(
930 $this->getRandomIdentifierIndex(), $otherElementList->getRandomIdentifierIndex()
931 );
932
933 return $differenceRandomIdentifierIndex;
934 }
935
939 public function completeContentsFromElementList(self $otherList)
940 {
941 foreach($this as $thisElement)
942 {
943 if( !$otherList->elementExistByRandomIdentifier($thisElement->getRandomIdentifier()) )
944 {
945 continue;
946 }
947
948 $otherElement = $otherList->getElementByRandomIdentifier(
949 $thisElement->getRandomIdentifier()
950 );
951
952 $thisElement->setContent( $otherElement->getContent() );
953 }
954 }
955
959 public function current() { return current($this->elements); }
960
964 public function next() { return next($this->elements); }
965
969 public function key() { return key($this->elements); }
970
974 public function valid() { return ($this->key() !== null); }
975
979 public function rewind() { return reset($this->elements); }
980
984 public static function getFallbackDefaultElement()
985 {
986 $element = new ilAssOrderingElement();
987 $element->setRandomIdentifier(self::FALLBACK_DEFAULT_ELEMENT_RANDOM_IDENTIFIER);
988
989 return $element;
990 }
991
997 public static function buildInstance($questionId, $orderingElements = array())
998 {
999 $elementList = new self();
1000
1001 $elementList->setQuestionId($questionId);
1002 $elementList->setElements($orderingElements);
1003
1004 return $elementList;
1005 }
1006}
$result
An exception for terminatinating execution or to throw for unit testing.
isIdentifierRegistered(ilAssOrderingElement $element, $identifierType)
registerIdentifiers(ilAssOrderingElement $element)
elementExistBySolutionIdentifier($solutionIdentifier)
getDifferenceElementList(self $otherElementList)
getElementBySolutionIdentifier($solutionIdentifier)
registerIdentifier(ilAssOrderingElement $element, $identifierType)
loadFromDb()
load elements from database
__construct()
ilAssOrderingElementList constructor.
getDifferenceRandomIdentifierIndex(self $otherElementList)
throwMissingReorderPositionException($randomIdentifier)
populateIdentifier(ilAssOrderingElement $element, $identifierType, $identifier)
removeElement(ilAssOrderingElement $removeElement)
static buildInstance($questionId, $orderingElements=array())
clearElementContents()
clears the contents of all elements
isValidIdentifier($identifierType, $identifier)
hasValidIdentifiers(ilAssOrderingElement $element)
ensureValidIdentifiers(ilAssOrderingElement $element)
fetchIdentifier(ilAssOrderingElement $element, $identifierType)
moveElementByPositions($currentPosition, $targetPosition)
addElement(ilAssOrderingElement $element)
__clone()
clone list by additionally cloning the element objects
ensureValidIdentifier(ilAssOrderingElement $element, $identifierType)
throwUnknownRandomIdentifiersException($randomIdentifiers)
resetElementsIndentations()
resets the indentation to level 0 for all elements in list
setRandomIdentifier($randomIdentifier)
setSolutionIdentifier($solutionIdentifier)
$GLOBALS['loaded']
Global hash that tracks already loaded includes.
global $ilDB