MDL-9506 Implemented grade_tree->update_db, fixed bugs in grade_category etc...
[moodle-pu.git] / lib / grade / grade_tree.php
blob7d7c97a71b2793cd30750ff761d28dc4d3d2f313
1 <?php // $Id$
3 ///////////////////////////////////////////////////////////////////////////
4 // //
5 // NOTICE OF COPYRIGHT //
6 // //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment //
8 // http://moodle.com //
9 // //
10 // Copyright (C) 2001-2003 Martin Dougiamas http://dougiamas.com //
11 // //
12 // This program is free software; you can redistribute it and/or modify //
13 // it under the terms of the GNU General Public License as published by //
14 // the Free Software Foundation; either version 2 of the License, or //
15 // (at your option) any later version. //
16 // //
17 // This program is distributed in the hope that it will be useful, //
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
20 // GNU General Public License for more details: //
21 // //
22 // http://www.gnu.org/copyleft/gpl.html //
23 // //
24 ///////////////////////////////////////////////////////////////////////////
26 /**
27 * This class represents a complete tree of categories, grade_items and final grades,
28 * organises as an array primarily, but which can also be converted to other formats.
29 * It has simple method calls with complex implementations, allowing for easy insertion,
30 * deletion and moving of items and categories within the tree.
31 */
32 class grade_tree {
33 /**
34 * The first sortorder for this tree, before any changes were made.
35 * @var int $first_sortorder
37 var $first_sortorder;
39 /**
40 * The basic representation of the tree as a hierarchical, 3-tiered array.
41 * @var array $tree_array
43 var $tree_array = array();
45 /**
46 * Another array with fillers for categories and items that do not have a parent, but have
47 * are not at level 2. This is used by the display_grades method.
48 * @var array $tree_filled
50 var $tree_filled = array();
52 /**
53 * An array of objects that need updating (normally just grade_item.sortorder).
54 * @var array $need_update
56 var $need_update = array();
58 /**
59 * An array of objects that need inserting in the DB.
60 * @var array $need_insert
62 var $need_insert = array();
64 /**
65 * An array of objects that need deleting from the DB.
66 * @var array $need_delete
68 var $need_delete = array();
71 /**
72 * Constructor, retrieves and stores a hierarchical array of all grade_category and grade_item
73 * objects for the given courseid or the entire site if no courseid given. Full objects are instantiated
74 * by default, but this can be switched off. The tree is indexed by sortorder, to facilitate CRUD operations
75 * and renumbering.
76 * @param int $courseid
77 * @param boolean $fullobjects
78 * @param array $tree
80 function grade_tree($courseid=NULL, $fullobjects=true, $tree=NULL) {
81 $this->courseid = $courseid;
82 if (!empty($tree)) {
83 $this->tree_array = $tree;
84 } else {
85 $this->tree_array = $this->get_tree($fullobjects);
88 $this->first_sortorder = key($this->tree_array);
91 /**
92 * Parses the array in search of a given sort order (the elements are indexed by
93 * sortorder), and returns a stdClass object with vital information about the
94 * element it has found.
95 * @param int $sortorder
96 * @return object element
98 function locate_element($sortorder) {
99 $topcatcount = 0;
100 $retval = false;
102 if (empty($this->tree_array)) {
103 debugging("grade_tree->tree_array was empty, I could not locate the element at sortorder $sortorder");
104 return false;
107 $level1count = 0;
109 foreach ($this->tree_array as $levelkey1 => $level1) {
110 $level1count++;
111 $level2count = 0;
112 $retval = new stdClass();
113 $retval->index = $levelkey1;
115 if ($levelkey1 == $sortorder) {
116 $retval->element = $level1;
117 $retval->position = $level1count;
118 return $retval;
121 if (!empty($level1['children'])) {
122 foreach ($level1['children'] as $level2key => $level2) {
123 $level2count++;
124 $level3count = 0;
126 $retval->index = "$levelkey1/$level2key";
127 if ($level2key == $sortorder) {
128 $retval->element = $level2;
129 $retval->position = $level2count;
130 return $retval;
133 if (!empty($level2['children'])) {
134 foreach ($level2['children'] as $level3key => $level3) {
135 $level3count++;
136 $retval->index = "$levelkey1/$level2key/$level3key";
138 if ($level3key == $sortorder) {
139 $retval->element = $level3;
140 $retval->position = $level3count;
141 return $retval;
148 return $retval;
152 * Given an element object, returns its type (topcat, subcat or item).
153 * The $element can be a straight object (fully instantiated), an array of 'object' and 'children'/'final_grades', or a stdClass element
154 * as produced by grade_tree::locate_element(). This method supports all three types of inputs.
155 * @param object $element
156 * @return string Type
158 function get_element_type($element) {
159 if (!empty($element->element['object'])) {
160 $object = $element->element['object'];
161 } elseif (!empty($element['object'])) {
162 $object = $element['object'];
163 } elseif (is_object($element)) {
164 $object = $element;
165 } else {
166 debugging("Invalid element given to grade_tree::get_element_type.");
167 return false;
170 if (get_class($object) == 'grade_item') {
171 return 'item';
172 } elseif (get_class($object) == 'grade_category') {
173 $object->get_children();
174 if (!empty($object->children)) {
175 $first_child = current($object->children);
176 if (get_class($first_child) == 'grade_item') {
177 return 'subcat';
178 } elseif (get_class($first_child) == 'grade_category') {
179 return 'topcat';
180 } else {
181 debugging("The category's first child was neither a category nor an item.");
182 return false;
184 } else {
185 debugging("The category did not have any children.");
186 return false;
188 } else {
189 debugging("Invalid element given to grade_tree::get_element_type.");
190 return false;
193 debugging("Could not determine the type of the given element.");
194 return false;
198 * Removes the given element (a stdClass object or a sortorder), remove_elements
199 * it from the tree. This does not renumber the tree. If a sortorder (int) is given, this
200 * method will first retrieve the referenced element from the tree, then re-run the method with that object.
201 * @var object $element An stdClass object typically returned by $this->locate(), or a sortorder (int)
202 * @return boolean
204 function remove_element($element) {
205 if (empty($this->first_sortorder)) {
206 $this->reset_first_sortorder();
209 if (isset($element->index)) {
210 // Decompose the element's index and build string for eval(unset) statement to follow
211 $indices = explode('/', $element->index);
212 $element_to_unset = '$this->tree_array[' . $indices[0] . ']';
214 if (isset($indices[1])) {
215 $element_to_unset .= "['children'][" . $indices[1] . ']';
218 if (isset($indices[2])) {
219 $element_to_unset .= "['children'][" . $indices[2] . ']';
222 eval("unset($element_to_unset);");
224 if (empty($element->element['object'])) {
225 debugging("Could not delete this element from the DB due to missing information.");
226 return false;
229 $this->need_delete[$element->element['object']->id] = $element->element['object'];
231 return true;
232 } else {
233 $element = $this->locate_element($element);
234 if (!empty($element)) {
235 return $this->remove_element($element);
236 } else {
237 debugging("The element you provided grade_tree::remove_element() is not valid.");
238 return false;
242 debugging("Unable to remove an element from the grade_tree.");
243 return false;
247 * Inserts an element in the tree. This can be either an array as returned by the grade_category methods, or
248 * an element object returned by grade_tree.
249 * @param mixed $element array or object. If object, the sub-tree is contained in $object->element
250 * @param int $destination_sortorder Where to insert the element
251 * @param string $position Either 'before' the destination_sortorder or 'after'
252 * @param boolean
254 function insert_element($element, $destination_sortorder, $position='before') {
255 if (empty($this->first_sortorder)) {
256 $this->reset_first_sortorder();
259 if ($position == 'before') {
260 $offset = -1;
261 } elseif ($position == 'after') {
262 $offset = 0;
263 } else {
264 debugging('move_element(..... $position) can only be "before" or "after", you gave ' . $position);
265 return false;
268 if (is_array($element)) {
269 $new_element = new stdClass();
270 $new_element->element = $element;
271 } elseif (is_object($element)) {
272 $new_element = $element;
275 // If the object is a grade_item, but the final_grades index isn't yet loaded, make the switch now. Same for grade_category and children
276 if (get_class($new_element->element['object']) == 'grade_item' && empty($new_element->element['final_grades'])) {
277 $new_element->element['final_grades'] = $new_element->element['object']->load_final();
278 unset($new_element->element['object']->grade_grades_final);
279 } elseif (get_class($new_element->element['object']) == 'grade_category' &&
280 empty($new_element->element['children']) &&
281 $new_element->element['object']->has_children()) {
282 $new_element->element['children'] = $new_element->element['object']->get_children(1);
283 unset($new_element->element['object']->children);
287 // TODO Problem when moving topcategories: sortorder gets reindexed when splicing the array
288 $destination_array = array($destination_sortorder => $new_element->element);
290 // Get the position of the destination element
291 $destination_element = $this->locate_element($destination_sortorder);
292 $position = $destination_element->position;
294 // Decompose the element's index and build string for eval(array_splice) statement to follow
295 $indices = explode('/', $destination_element->index);
297 if (empty($indices)) {
298 debugging("The destination element did not have a valid index (as assigned by grade_tree::locate_element).");
299 return false;
302 $element_to_splice = '$this->tree_array';
304 if (isset($indices[1])) {
305 $element_to_splice .= '[' . $indices[0] . "]['children']";
308 if (isset($indices[2])) {
309 $element_to_splice .= '[' . $indices[1] . "]['children']";
312 eval("array_splice($element_to_splice, \$position + \$offset, 0, \$destination_array);");
314 if (!is_object($new_element)) {
315 debugging("Could not insert this element into the DB due to missing information.");
316 return false;
319 $this->need_insert[$new_element->element['object']->id] = $new_element->element['object'];
321 return true;
325 * Moves an existing element in the tree to another position OF EQUAL LEVEL. This
326 * constraint is essential and very important.
327 * @param int $source_sortorder The sortorder of the element to move
328 * @param int $destination_sortorder The sortorder where the element will go
329 * @param string $position Either 'before' the destination_sortorder or 'after' it
330 * @return boolean
332 function move_element($source_sortorder, $destination_sortorder, $position='before') {
333 if (empty($this->first_sortorder)) {
334 $this->reset_first_sortorder();
337 // Locate the position of the source element in the tree
338 $source = $this->locate_element($source_sortorder);
340 // Remove this element from the tree
341 $this->remove_element($source);
343 $destination = $this->locate_element($destination_sortorder);
345 // Insert the element before the destination sortorder
346 $this->insert_element($source, $destination_sortorder, $position);
348 return true;
352 * Uses the key of the first entry in this->tree_array to reset the first_sortorder of this tree. Essential
353 * after each renumbering.
355 function reset_first_sortorder() {
356 if (count($this->tree_array) < 1) {
357 debugging("Cannot reset the grade_tree's first_sortorder because the tree_array hasn't been loaded or is empty.");
358 return false;
360 reset($this->tree_array);
361 $this->first_sortorder = key($this->tree_array);
362 return $this->first_sortorder;
366 * One at a time, re-assigns new sort orders for every element in the tree, starting
367 * with a base number.
368 * @return array A debugging array which shows the progression of variables throughout this method. This is very useful
369 * to identify problems and implement new functionality.
371 function renumber($starting_sortorder=NULL) {
372 $sortorder = $starting_sortorder;
374 if (empty($starting_sortorder)) {
375 if (empty($this->first_sortorder)) {
376 debugging("The tree's first_order variable isn't set, you must provide a starting_sortorder to the renumber method.");
377 return false;
379 $sortorder = $this->first_sortorder - 1;
382 $newtree = array();
383 $topcatsortorder = 0;
384 $debug = array();
386 foreach ($this->tree_array as $topcat) {
387 $sortorder++;
388 $subcatsortorder = 0;
390 $debug[] = array('sortorder' => $sortorder,
391 'need_update' => $this->need_update,
392 'line' => __LINE__);
394 if (!empty($topcat['children'])) {
395 $topcatsortorder = $sortorder;
396 $debug[] = array('sortorder' => $sortorder,
397 'topcatsortorder' => $topcatsortorder,
398 'need_update' => $this->need_update,
399 'line' => __LINE__);
401 foreach ($topcat['children'] as $subcat) {
402 $sortorder++;
403 $debug[] = array('sortorder' => $sortorder,
404 'topcatsortorder' => $topcatsortorder,
405 'need_update' => $this->need_update,
406 'line' => __LINE__);
408 if (!empty($subcat['children'])) {
409 $subcatsortorder = $sortorder;
411 $debug[] = array('sortorder' => $sortorder,
412 'topcatsortorder' => $topcatsortorder,
413 'subcatsortorder' => $subcatsortorder,
414 'need_update' => $this->need_update,
415 'line' => __LINE__);
417 foreach ($subcat['children'] as $item) {
418 $sortorder++;
420 $debug[] = array('sortorder' => $sortorder,
421 'topcatsortorder' => $topcatsortorder,
422 'subcatsortorder' => $subcatsortorder,
423 'need_update' => $this->need_update,
424 'line' => __LINE__);
426 $newtree[$topcatsortorder]['children'][$subcatsortorder]['children'][$sortorder] = $item;
428 if ($sortorder != $item['object']->sortorder) {
429 $this->need_update[$item['object']->id] = array('old_sortorder' => $item['object']->sortorder, 'new_sortorder' => $sortorder);
430 $debug[] = array('sortorder' => $sortorder,
431 'topcatsortorder' => $topcatsortorder,
432 'subcatsortorder' => $subcatsortorder,
433 'need_update' => $this->need_update,
434 'line' => __LINE__);
438 $newtree[$topcatsortorder]['children'][$subcatsortorder]['object'] = $subcat['object'];
439 $newsortorder = $subcatsortorder;
440 } else {
441 $newtree[$topcatsortorder]['children'][$sortorder] = $subcat;
442 $newsortorder = $sortorder;
445 if ($newsortorder != $subcat['object']->sortorder) {
446 $this->need_update[$subcat['object']->id] = array('old_sortorder' => $subcat['object']->sortorder, 'new_sortorder' => $newsortorder);
447 $debug[] = array('sortorder' => $sortorder,
448 'topcatsortorder' => $topcatsortorder,
449 'subcatsortorder' => $subcatsortorder,
450 'need_update' => $this->need_update,
451 'line' => __LINE__);
455 $newtree[$topcatsortorder]['object'] = $topcat['object'];
456 $newsortorder = $topcatsortorder;
457 } else {
458 $newsortorder = $sortorder;
459 $newtree[$sortorder] = $topcat;
462 if ($newsortorder != $topcat['object']->sortorder) {
463 $this->need_update[$topcat['object']->id] = array('old_sortorder' => $topcat['object']->sortorder, 'new_sortorder' => $newsortorder);
464 $debug[] = array('sortorder' => $sortorder,
465 'topcatsortorder' => $topcatsortorder,
466 'subcatsortorder' => $subcatsortorder,
467 'need_update' => $this->need_update,
468 'line' => __LINE__);
473 $this->tree_array = $newtree;
474 unset($this->first_sortorder);
475 $this->build_tree_filled();
476 return $debug;
480 * Static method that returns a sorted, nested array of all grade_categories and grade_items for
481 * a given course, or for the entire site if no courseid is given.
482 * @param boolean $fullobjects Whether to instantiate full objects based on the data or not
483 * @return array
485 function get_tree($fullobjects=true) {
486 global $CFG;
487 global $db;
488 $db->debug = false;
489 $tree = array();
490 $fillers = array();
492 $category_table = $CFG->prefix . 'grade_categories';
493 $items_table = $CFG->prefix . 'grade_items';
495 $catconstraint = '';
496 $itemconstraint = '';
498 if (!empty($this->courseid)) {
499 $catconstraint = " AND $category_table.courseid = $this->courseid ";
500 $itemconstraint = " AND $items_table.courseid = $this->courseid ";
503 // Get ordered list of grade_items (not category type)
504 $query = "SELECT * FROM $items_table WHERE itemtype <> 'category' $itemconstraint ORDER BY sortorder";
505 $grade_items = get_records_sql($query);
507 // For every grade_item that doesn't have a parent category, create category fillers
508 foreach ($grade_items as $itemid => $item) {
509 if (empty($item->categoryid)) {
510 if ($fullobjects) {
511 $item = new grade_item($item);
513 $fillers[$item->sortorder] = $item;
517 // Get all top categories
518 $query = "SELECT $category_table.*, sortorder FROM $category_table, $items_table
519 WHERE iteminstance = $category_table.id $catconstraint ORDER BY sortorder";
521 $topcats = get_records_sql($query);
523 if (empty($topcats)) {
524 return null;
527 // If any of these categories has grade_items as children, create a topcategory filler with colspan=count(children)
528 foreach ($topcats as $topcatid => $topcat) {
529 $topcatobject = new grade_category($topcat, false);
530 if ($topcatobject->get_childrentype() == 'grade_item' && empty($topcatobject->parent)) {
531 $topcatobject->childrencount = $topcatobject->has_children();
532 $fillers[$topcat->sortorder] = $topcatobject;
533 unset($topcats[$topcatid]);
537 foreach ($topcats as $topcatid => $topcat) {
538 // Check the fillers array, see if one must be inserted before this topcat
539 if (key($fillers) < $topcat->sortorder) {
540 $sortorder = key($fillers);
541 $object = current($fillers);
542 unset($fillers[$sortorder]);
544 $this->tree_filled[$sortorder] = $this->get_filler($object, $fullobjects);
545 $element = array();
547 if (get_class($object) == 'grade_category') {
548 $children = $object->get_children(1);
549 unset($object->children);
550 $element['children'] = $children;
551 } elseif (get_class($object) == 'grade_item') {
552 $final_grades = $object->get_final();
553 unset($object->grade_grades_final);
554 $element['final_grades'] = $final_grades;
557 $object->sortorder = $sortorder;
558 $element['object'] = $object;
559 $tree[$sortorder] = $element;
562 $query = "SELECT $category_table.*, sortorder FROM $category_table, $items_table
563 WHERE iteminstance = $category_table.id AND parent = $topcatid ORDER BY sortorder";
564 $subcats = get_records_sql($query);
565 $subcattree = array();
567 if (empty($subcats)) {
568 continue;
571 foreach ($subcats as $subcatid => $subcat) {
572 $itemtree = array();
573 $items = get_records('grade_items', 'categoryid', $subcatid, 'sortorder');
575 if (empty($items)) {
576 continue;
579 foreach ($items as $itemid => $item) {
580 $finaltree = array();
582 if ($fullobjects) {
583 $final = new grade_grades_final();
584 $final->itemid = $itemid;
585 $finals = $final->fetch_all_using_this();
586 } else {
587 $finals = get_records('grade_grades_final', 'itemid', $itemid);
590 if ($fullobjects) {
591 $sortorder = $item->sortorder;
592 $item = new grade_item($item);
593 $item->sortorder = $sortorder;
596 $itemtree[$item->sortorder] = array('object' => $item, 'finalgrades' => $finals);
599 if ($fullobjects) {
600 $sortorder = $subcat->sortorder;
601 $subcat = new grade_category($subcat, false);
602 $subcat->sortorder = $sortorder;
604 $subcattree[$subcat->sortorder] = array('object' => $subcat, 'children' => $itemtree);
607 if ($fullobjects) {
608 $sortorder = $topcat->sortorder;
609 $topcat = new grade_category($topcat, false);
610 $topcat->sortorder = $sortorder;
613 $tree[$topcat->sortorder] = array('object' => $topcat, 'children' => $subcattree);
614 $this->tree_filled[$topcat->sortorder] = array('object' => $topcat, 'children' => $subcattree);
617 // If there are still grade_items or grade_categories without a top category, add another filler
618 if (!empty($fillers)) {
619 foreach ($fillers as $sortorder => $object) {
620 $this->tree_filled[$sortorder] = $this->get_filler($object, $fullobjects);
622 if (get_class($object) == 'grade_category') {
623 $children = $object->get_children(1);
624 unset($object->children);
625 $element['children'] = $children;
626 } elseif (get_class($object) == 'grade_item') {
627 $final_grades = $object->get_final();
628 unset($object->grade_grades_final);
629 $element['final_grades'] = $final_grades;
632 $object->sortorder = $sortorder;
633 $element['object'] = $object;
634 $tree[$sortorder] = $element;
638 $db->debug = false;
639 return $tree;
643 * Returns a hierarchical array, prefilled with the values needed to populate
644 * the tree of grade_items in the cases where a grade_item or grade_category doesn't have a
645 * 2nd level topcategory.
646 * @param object $object A grade_item or a grade_category object
647 * @param boolean $fullobjects Whether to instantiate full objects or just return stdClass objects
648 * @return array
650 function get_filler($object, $fullobjects=true) {
651 $filler_array = array();
653 // Depending on whether the filler is for a grade_item or a category...
654 if (isset($object->itemname)) {
655 if (get_class($object) == 'grade_item') {
656 $finals = $object->load_final();
657 } else {
658 $item_object = new grade_item($object, false);
659 $finals = $object->load_final();
662 $filler_array = array('object' => 'filler', 'children' =>
663 array(0 => array('object' => 'filler', 'children' =>
664 array(0 => array('object' => $object, 'finalgrades' => $finals)))));
665 } elseif (method_exists($object, 'get_children')) {
667 $subcat_children = $object->get_children(0, 'flat');
668 $children_for_tree = array();
669 foreach ($subcat_children as $itemid => $item) {
670 $finals = null;
672 if (get_class($item) == 'grade_item') {
673 $finals = $item->load_final();
674 } else {
675 $item_object = new grade_item($item, false);
676 if (method_exists($item, 'load_final')) {
677 $finals = $item->load_final();
681 $children_for_tree[$itemid] = array('object' => $item, 'finalgrades' => $finals);
684 $filler_array = array('object' => 'filler', 'colspan' => $object->childrencount, 'children' =>
685 array(0 => array('object' => $object, 'children' => $children_for_tree)));
688 return $filler_array;
692 * Returns a HTML table with all the grades in the course requested, or all the grades in the site.
693 * IMPORTANT: This method (and its associated methods) assumes that we are using only 2 levels of categories (topcat and subcat)
694 * @todo Return extra column for students
695 * @todo Return a row of final grades for each student
696 * @todo Return icons
697 * @todo Return totals
698 * @todo Return row below headers for grading range
699 * @return string HTML table
701 function display_grades() {
702 // 1. Fetch all top-level categories for this course, with all children preloaded, sorted by sortorder
703 $tree = $this->tree_filled;
705 if (empty($this->tree_filled)) {
706 debugging("The tree_filled array wasn't initialised, grade_tree could not display the grades correctly.");
707 return false;
710 $topcathtml = '<tr>';
711 $cathtml = '<tr>';
712 $itemhtml = '<tr>';
714 foreach ($tree as $topcat) {
715 $itemcount = 0;
717 foreach ($topcat['children'] as $catkey => $cat) {
718 $catitemcount = 0;
720 foreach ($cat['children'] as $item) {
721 $itemcount++;
722 $catitemcount++;
723 $itemhtml .= '<td>' . $item['object']->itemname . '</td>';
726 if ($cat['object'] == 'filler') {
727 $cathtml .= '<td class="subfiller">&nbsp;</td>';
728 } else {
729 $cat['object']->load_grade_item();
730 $cathtml .= '<td colspan="' . $catitemcount . '">' . $cat['object']->fullname . '</td>';
734 if ($topcat['object'] == 'filler') {
735 $colspan = null;
736 if (!empty($topcat['colspan'])) {
737 $colspan = 'colspan="' . $topcat['colspan'] . '" ';
739 $topcathtml .= '<td ' . $colspan . 'class="topfiller">&nbsp;</td>';
740 } else {
741 $topcathtml .= '<th colspan="' . $itemcount . '">' . $topcat['object']->fullname . '</th>';
746 $itemhtml .= '</tr>';
747 $cathtml .= '</tr>';
748 $topcathtml .= '</tr>';
750 return "<table style=\"text-align: center\" border=\"1\">$topcathtml$cathtml$itemhtml</table>";
755 * Using $this->tree_array, builds $this->tree_filled, which is the same array but with fake categories as
756 * fillers. These are used by display_grades, to print out empty cells over orphan grade_items and grade_categories.
757 * @return boolean Success or Failure.
759 function build_tree_filled() {
760 if (empty($this->tree_array)) {
761 debugging("You cannot build the tree_filled array until the tree_array is filled.");
762 return false;
765 $this->tree_filled = array();
767 foreach ($this->tree_array as $level1order => $level1) {
768 if ($this->get_element_type($level1) == 'item' || $this->get_element_type($level1) == 'subcat') {
769 $this->tree_filled[$level1order] = $this->get_filler($level1['object']);
770 } else {
771 $this->tree_filled[$level1order] = $level1;
775 reset($this->tree_array);
777 return true;
781 * Performs any delete, insert or update queries required, depending on the objects
782 * stored in $this->need_update, need_insert and need_delete.
783 * @return boolean Success or Failure
785 function update_db() {
786 // Perform deletions first
787 foreach ($this->need_delete as $id => $object) {
788 // If an item is both in the delete AND insert arrays, it must be an existing object that only needs updating, so ignore it.
789 if (empty($this->need_insert[$id])) {
790 if (!$object->delete()) {
791 debugging("Could not delete object from DB.");
796 foreach ($this->need_insert as $id => $object) {
797 if (empty($this->need_delete[$id])) {
798 if (!$object->insert()) {
799 debugging("Could not insert object into DB.");
804 $this->need_delete = array();
805 $this->need_insert = array();
807 foreach ($this->need_update as $id => $sortorders) {
808 if (!set_field('grade_items', 'sortorder', $sortorders['new_sortorder'], 'id', $id)) {
809 debugging("Could not update the grade_item's sortorder in DB.");
813 $this->need_update = array();