MDL-63619 tool_dataprivacy: Performance improvement
authorAndrew Nicols <andrew@nicols.co.uk>
Wed, 17 Oct 2018 12:53:16 +0000 (20:53 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Wed, 24 Oct 2018 00:48:18 +0000 (08:48 +0800)
admin/tool/dataprivacy/classes/data_registry.php

index 2865aa0..50681ba 100644 (file)
@@ -177,60 +177,92 @@ class data_registry {
      * @return persistent|false It return a 'purpose' instance or a 'category' instance, depending on $element
      */
     public static function get_effective_context_value(\context $context, $element, $forcedvalue = false) {
+        global $DB;
+
         if ($element !== 'purpose' && $element !== 'category') {
             throw new coding_exception('Only \'purpose\' and \'category\' are supported.');
         }
         $fieldname = $element . 'id';
 
+        if (!empty($forcedvalue) && ($forcedvalue === context_instance::INHERIT)) {
+            // Do not include the current context when calculating the value.
+            // This has the effect that an inheritted value is calculated.
+            $parentcontextids = $context->get_parent_context_ids(false);
+        } else if (!empty($forcedvalue) && ($forcedvalue !== context_instance::NOTSET)) {
+            return self::get_element_instance($element, $forcedvalue);
+        } else {
+            // Fetch all parent contexts, including self.
+            $parentcontextids = $context->get_parent_context_ids(true);
+        }
+        list($insql, $inparams) = $DB->get_in_or_equal($parentcontextids, SQL_PARAMS_NAMED);
+        $inparams['contextmodule'] = CONTEXT_MODULE;
+
+        if ('purpose' === $element) {
+             $elementjoin = 'LEFT JOIN {tool_dataprivacy_purpose} ele ON ctxins.purposeid = ele.id';
+             $elementfields = purpose::get_sql_fields('ele', 'ele');
+        } else {
+             $elementjoin = 'LEFT JOIN {tool_dataprivacy_category} ele ON ctxins.categoryid = ele.id';
+             $elementfields = category::get_sql_fields('ele', 'ele');
+        }
+        $contextfields = \context_helper::get_preload_record_columns_sql('ctx');
+        $fields = implode(', ', ['ctx.id', 'm.name AS modname', $contextfields, $elementfields]);
+
+        $sql = "SELECT $fields
+                  FROM {context} ctx
+             LEFT JOIN {tool_dataprivacy_ctxinstance} ctxins ON ctx.id = ctxins.contextid
+             LEFT JOIN {course_modules} cm ON ctx.contextlevel = :contextmodule AND ctx.instanceid = cm.id
+             LEFT JOIN {modules} m ON m.id = cm.module
+             {$elementjoin}
+                 WHERE ctx.id {$insql}
+              ORDER BY ctx.path DESC";
+        $contextinstances = $DB->get_records_sql($sql, $inparams);
+
         // Check whether this context is a user context, or a child of a user context.
-        // User contexts share the same context and cannot be set individually.
-        $parents = $context->get_parent_contexts(true);
-        foreach ($parents as $parent) {
+        // All children of a User context share the same context and cannot be set individually.
+        foreach ($contextinstances as $record) {
+            \context_helper::preload_from_record($record);
+            $parent = \context::instance_by_id($record->id, false);
+
             if ($parent->contextlevel == CONTEXT_USER) {
                 // Use the context level value for the user.
                 return self::get_effective_contextlevel_value(CONTEXT_USER, $element);
             }
         }
 
-        $instancevalue = context_instance::NOTSET;
-        if (empty($forcedvalue)) {
-            if ($instance = context_instance::get_record_by_contextid($context->id, false)) {
-                $instancevalue = $instance->get($fieldname);
+        foreach ($contextinstances as $record) {
+            $parent = \context::instance_by_id($record->id, false);
+
+            $checkcontextlevel = false;
+            if (empty($record->eleid)) {
+                $checkcontextlevel = true;
             }
-        } else {
-            $instancevalue = $forcedvalue;
-        }
 
-        // Not set.
-        if ($instancevalue == context_instance::NOTSET) {
-            // Check if we need to pass the plugin name of an activity.
-            $forplugin = '';
-            if ($context->contextlevel == CONTEXT_MODULE) {
-                list($course, $cm) = get_course_and_cm_from_cmid($context->instanceid);
-                $forplugin = $cm->modname;
+            if (!empty($forcedvalue) && context_instance::NOTSET === $forcedvalue) {
+                $checkcontextlevel = true;
             }
 
-            // Use the default context level value.
-            list($purposeid, $categoryid) = self::get_effective_default_contextlevel_purpose_and_category(
-                $context->contextlevel, false, false, $forplugin
-            );
-            $instancevalue = $$fieldname;
-        }
+            if ($checkcontextlevel) {
+                // Check for a value at the contextlevel
+                $forplugin = empty($record->modname) ? '' : $record->modname;
+                list($purposeid, $categoryid) = self::get_effective_default_contextlevel_purpose_and_category(
+                        $parent->contextlevel, false, false, $forplugin);
 
-        if (context_instance::NOTSET !== $instancevalue && context_instance::INHERIT !== $instancevalue) {
-            // There is an actual value. Return it.
-            return self::get_element_instance($element, $instancevalue);
-        }
+                $instancevalue = $$fieldname;
 
-        // There is no value set (or it is set to inherit).
-        // Fetch from the parent context.
-        $parent = $context->get_parent_context();
+                if (context_instance::NOTSET !== $instancevalue && context_instance::INHERIT !== $instancevalue) {
+                    // There is an actual value. Return it.
+                    return self::get_element_instance($element, $instancevalue);
+                }
+            } else {
+                $elementclass = "\\tool_dataprivacy\\{$element}";
+                $instance = new $elementclass(null, $elementclass::extract_record($record, 'ele'));
+                $instance->validate();
 
-        if (CONTEXT_SYSTEM == $parent->contextlevel) {
-            return self::get_effective_contextlevel_value(CONTEXT_SYSTEM, $element);
-        } else {
-            return self::get_effective_context_value($context->get_parent_context(), $element);
+                return $instance;
+            }
         }
+
+        throw new coding_exception('Something went wrong, system defaults should be set and we should already have a value.');
     }
 
     /**