Merge branch 'MDL-63686-master' of git://github.com/andrewnicols/moodle
authorJake Dallimore <jake@moodle.com>
Tue, 8 Jan 2019 06:37:31 +0000 (14:37 +0800)
committerJake Dallimore <jake@moodle.com>
Tue, 8 Jan 2019 06:37:31 +0000 (14:37 +0800)
lib/accesslib.php
lib/tests/accesslib_test.php

index 9d6dc4f..1cd44df 100644 (file)
@@ -5434,6 +5434,9 @@ abstract class context extends stdClass implements IteratorAggregate {
             return array();
         }
 
+        // Preload the contexts to reduce DB calls.
+        context_helper::preload_contexts_by_id($contextids);
+
         $result = array();
         foreach ($contextids as $contextid) {
             $parent = context::instance_by_id($contextid, MUST_EXIST);
@@ -5870,6 +5873,35 @@ class context_helper extends context {
          context::preload_from_record($rec);
      }
 
+    /**
+     * Preload a set of contexts using their contextid.
+     *
+     * @param   array $contextids
+     */
+    public static function preload_contexts_by_id(array $contextids) {
+        global $DB;
+
+        // Determine which contexts are not already cached.
+        $tofetch = [];
+        foreach ($contextids as $contextid) {
+            if (!self::cache_get_by_id($contextid)) {
+                $tofetch[] = $contextid;
+            }
+        }
+
+        if (count($tofetch) > 1) {
+            // There are at least two to fetch.
+            // There is no point only fetching a single context as this would be no more efficient than calling the existing code.
+            list($insql, $inparams) = $DB->get_in_or_equal($tofetch, SQL_PARAMS_NAMED);
+            $ctxs = $DB->get_recordset_select('context', "id {$insql}", $inparams, '',
+                    \context_helper::get_preload_record_columns_sql('{context}'));
+            foreach ($ctxs as $ctx) {
+                self::preload_from_record($ctx);
+            }
+            $ctxs->close();
+        }
+    }
+
     /**
      * Preload all contexts instances from course.
      *
index 2541844..287531a 100644 (file)
@@ -3820,6 +3820,47 @@ class core_accesslib_testcase extends advanced_testcase {
         $this->setUser($user2);
         $this->assertEquals($expectedteacher, get_profile_roles($coursecontext));
     }
+
+    /**
+     * Ensure that the get_parent_contexts() function limits the number of queries it performs.
+     */
+    public function test_get_parent_contexts_preload() {
+        global $DB;
+
+        $this->resetAfterTest();
+
+        /*
+         * Given the following data structure:
+         * System
+         * - Category
+         * --- Category
+         * ----- Category
+         * ------- Category
+         * --------- Course
+         * ----------- Activity (Forum)
+         */
+
+        $contexts = [];
+
+        $cat1 = $this->getDataGenerator()->create_category();
+        $cat2 = $this->getDataGenerator()->create_category(['parent' => $cat1->id]);
+        $cat3 = $this->getDataGenerator()->create_category(['parent' => $cat2->id]);
+        $cat4 = $this->getDataGenerator()->create_category(['parent' => $cat3->id]);
+        $course = $this->getDataGenerator()->create_course(['category' => $cat4->id]);
+        $forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id]);
+
+        $modcontext = context_module::instance($forum->cmid);
+
+        context_helper::reset_caches();
+
+        // There should only be a single DB query.
+        $predbqueries = $DB->perf_get_reads();
+
+        $parents = $modcontext->get_parent_contexts();
+        // Note: For some databases There is one read, plus one FETCH, plus one CLOSE.
+        // These all show as reads, when there has actually only been a single query.
+        $this->assertLessThanOrEqual(3, $DB->perf_get_reads() - $predbqueries);
+    }
 }
 
 /**