]> git.mjollnir.org Git - moodle.git/commitdiff
datalib: Introducing get_courses_wmanagers() for print_courses() usage
authormartinlanghoff <martinlanghoff>
Wed, 19 Sep 2007 07:26:42 +0000 (07:26 +0000)
committermartinlanghoff <martinlanghoff>
Wed, 19 Sep 2007 07:26:42 +0000 (07:26 +0000)
For an efficient print_courses() we need to grab in a constant number
of queries...

 - course data
 - "course manager" role assignments
 - user records for the coursemanagers' fullname()

So here we do it in 2 DB queries. The 2nd one (grabbing RAs and user
records) can be expensive if we are dealing with a large number of
courses.

Which we shouldn't - When the number of courses is large the course
listing doesn't come this way anyway...

lib/datalib.php

index 5e5e4d8e91d0b596009f96e85e838056cca051dd..50a71f7a2b1b1a5e21700c12b7a2cb042693620b 100644 (file)
@@ -591,6 +591,205 @@ function get_courses_page($categoryid="all", $sort="c.sortorder ASC", $fields="c
     */
 }
 
+/*
+ * Retrieve course records with the course managers and other related records
+ * that we need for print_course(). This allows print_courses() to do its job
+ * in a constant number of DB queries, regardless of the number of courses,
+ * role assignments, etc.
+ * 
+ * The returned array is indexed on c.id, and each course will have
+ * - $course->context - a context obj
+ * - $course->managers - array containing RA objects that include a $user obj
+ *                       with the minimal fields needed for fullname()
+ *
+ */
+function get_courses_wmanagers($categoryid=0, $sort="c.sortorder ASC", $fields=array()) {
+    /*
+     * The plan is to 
+     *
+     * - Grab the courses JOINed w/context
+     *
+     * - Grab the interesting course-manager RAs
+     *   JOINed with a base user obj and add them to each course
+     *
+     * So as to do all the work in 2 DB queries. The RA+user JOIN
+     * ends up being pretty expensive if it happens over _all_
+     * courses on a large site. (Are we surprised!?)
+     *
+     * So this should _never_ get called with 'all' on a large site.
+     *
+     */
+    global $USER, $CFG;
+
+    $allcats = false; // bool flag
+    if ($categoryid === 'all') {
+        $categoryclause   = '';
+        $allcats = true;
+    } elseif (is_numeric($categoryid)) {
+        $categoryclause = "c.category = $categoryid";
+    } else {
+        debugging("Could not recognise categoryid = $categoryid");
+        $categoryclause = '';
+    }
+
+    $basefields = array('id', 'category', 'sortorder',
+                        'shortname', 'fullname', 'idnumber',
+                        'teacher', 'teachers', 'student', 'students',
+                        'guest', 'startdate', 'visible',
+                        'newsitems',  'cost', 'enrol',
+                        'groupmode', 'groupmodeforce');
+
+    if (!is_null($fields) && is_string($fields)) {
+        if (empty($fields)) {
+            $fields = $basefields;
+        } else {
+            // turn the fields from a string to an array that 
+            // get_user_courses_bycap() will like...
+            $fields = explode(',',$fields);
+            $fields = array_map('trim', $fields);
+            $fields = array_unique(array_merge($basefields, $fields));
+        }
+    } elseif (is_array($fields)) {
+        $fields = array_merge($basefields,$fields);
+    }
+    $coursefields = 'c.' .join(',c.', $fields);
+
+    if (empty($sort)) {
+        $sortstatement = "";
+    } else {
+        $sortstatement = "ORDER BY $sort";
+    }
+
+    $where = '';
+    if ($categoryclause !== ''){
+        $where = "WHERE $categoryclause";
+    }
+
+    // pull out all courses matching the cat
+    $sql = "SELECT $coursefields,
+                   ctx.id AS ctxid, ctx.path AS ctxpath, ctx.depth as ctxdepth
+            FROM {$CFG->prefix}course c
+            JOIN {$CFG->prefix}context ctx
+                 ON (c.id=ctx.instanceid AND ctx.contextlevel=".CONTEXT_COURSE.")
+                 $where
+                 $sortstatement";
+
+    $catpaths = array();
+    $catpath  = NULL;
+    if ($courses = get_records_sql($sql)) {
+        // loop on courses materialising
+        // the context, and prepping data to fetch the 
+        // managers efficiently later...
+        foreach ($courses as $k => $course) {
+            $courses[$k] = make_context_subobj($courses[$k]);
+            $courses[$k]->managers = array();
+            if ($allcats === false) {
+                // single cat, so take just the first one...
+                if ($catpath === NULL) {
+                    $catpath = preg_replace(':/\d+$:', '',$courses[$k]->context->path);
+                }
+            } else {
+                // chop off the contextid of the course itself
+                // like dirname() does...
+                $catpaths[] = preg_replace(':/\d+$:', '',$courses[$k]->context->path);
+            }
+        }
+    } else {
+        return array(); // no courses!
+    }
+
+    $managerroles = split(',', $CFG->coursemanager);
+    $catctxids = '';
+    if (count($managerroles)) {
+        if ($allcats === true) {
+            $catpaths  = array_unique($catpaths);
+            $ctxids = array();
+            foreach ($catpaths as $cpath) {
+                $ctxids = array_merge($ctxids, explode('/',substr($cpath,1)));
+            }
+            $ctxids = array_unique($ctxids);
+            $catctxids = implode( ',' , $ctxids);
+            unset($catpaths);unset($cpath);
+        } else {
+            // take the ctx path from the first course
+            // as all categories will be the same...
+            $catpath = substr($catpath,1);
+            $catpath = preg_replace(':/\d+$:','',$catpath);
+            $catctxids = str_replace('/',',',$catpath);
+        }
+        if ($categoryclause !== '') {
+            $categoryclause = "AND $categoryclause";
+        }
+        /*
+         * Note: Here we use a LEFT OUTER JOIN that can 
+         * "optionally" match to avoid passing a ton of context
+         * ids in an IN() clause. Perhaps a subselect is faster.
+         *
+         * In any case, this SQL is not-so-nice over large sets of
+         * courses with no $categoryclause.
+         *
+         */
+        $sql = "SELECT ctx.path, ctx.instanceid, ctx.contextlevel,
+                       ra.hidden, 
+                       r.id AS roleid, r.name as rolename,
+                       u.id AS userid, u.firstname, u.lastname
+                FROM {$CFG->prefix}role_assignments ra
+                JOIN {$CFG->prefix}context ctx
+                  ON ra.contextid = ctx.id
+                JOIN {$CFG->prefix}user u
+                  ON ra.userid = u.id
+                JOIN {$CFG->prefix}role r
+                  ON ra.roleid = r.id
+                LEFT OUTER JOIN {$CFG->prefix}course c
+                  ON (ctx.instanceid=c.id AND ctx.contextlevel=".CONTEXT_COURSE.")
+                WHERE (  c.id IS NOT NULL 
+                         OR ra.contextid  IN ($catctxids) )
+                      AND ra.roleid IN ({$CFG->coursemanager})
+                      $categoryclause
+                ORDER BY r.sortorder ASC, ctx.contextlevel ASC, ra.sortorder ASC";
+
+        $rs = get_recordset_sql($sql);
+        
+        // This loop is fairly stupid as it stands - might get better
+        // results doing an initial pass clustering RAs by path.
+        if ($rs->RecordCount()) {
+            while ($ra = rs_fetch_next_record($rs)) {
+                $user = new StdClass;
+                $user->id        = $ra->userid;    unset($ra->userid);
+                $user->firstname = $ra->firstname; unset($ra->firstname);
+                $user->lastname  = $ra->lastname;  unset($ra->lastname);
+                $ra->user = $user;
+                if ($ra->contextlevel == CONTEXT_SYSTEM) {
+                    foreach ($courses as $k => $course) {
+                        $courses[$k]->managers[] = $ra;
+                    }
+                } elseif ($ra->contextlevel == CONTEXT_COURSECAT) {
+                    if ($allcats === false) {
+                        // It always applies
+                        foreach ($courses as $k => $course) {
+                            $courses[$k]->managers[] = $ra;
+                        }
+                    } else {
+                        foreach ($courses as $k => $course) {
+                            // Note that strpos() returns 0 as "matched at pos 0"
+                            if (strpos($course->context->path, $ra->path.'/')===0) {
+                                // Only add it to subpaths
+                                $courses[$k]->managers[] = $ra;
+                            }
+                        }
+                    }
+                } else { // course-level
+                    $courses[$ra->instanceid]->managers[] = $ra;
+                }
+            }
+        }
+
+
+        
+    }
+
+    return $courses;
+}
 
 /**
  * Convenience function - lists courses that a user has access to view.