Skip to content

Commit 62ca1ad

Browse files
committed
MDL-86493 mod_quiz: Optimise mod_quiz_cm_info_dynamic
1 parent 74ee977 commit 62ca1ad

File tree

8 files changed

+330
-285
lines changed

8 files changed

+330
-285
lines changed

public/mod/quiz/classes/cache/overrides.php

Lines changed: 0 additions & 115 deletions
This file was deleted.
Lines changed: 107 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?php
2-
// This file is part of Moodle - http://moodle.org/
2+
// This file is part of Moodle - https://moodle.org/
33
//
44
// Moodle is free software: you can redistribute it and/or modify
55
// it under the terms of the GNU General Public License as published by
@@ -12,115 +12,159 @@
1212
// GNU General Public License for more details.
1313
//
1414
// You should have received a copy of the GNU General Public License
15-
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
15+
// along with Moodle. If not, see <https://www.gnu.org/licenses/>.
1616

1717
namespace mod_quiz\local;
1818

19+
use cache;
20+
use core_cache\data_source_interface;
21+
use core_cache\definition;
22+
1923
/**
20-
* Cache manager for quiz overrides
24+
* Cache encapsulation for quiz overrides.
2125
*
22-
* Override cache data is set via its data source, {@see \mod_quiz\cache\overrides}
23-
* @package mod_quiz
24-
* @copyright 2024 Matthew Hilton <[email protected]>
25-
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26+
* @package mod_quiz
27+
* @copyright 2025 Catalyst IT Australia Pty Ltd
28+
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2629
*/
27-
class override_cache {
28-
/** @var string invalidation event used to purge data when reset_userdata is called, {@see \cache_helper::purge_by_event()} **/
29-
public const INVALIDATION_USERDATARESET = 'userdatareset';
30+
class override_cache implements data_source_interface {
31+
/**
32+
* @var string Invalidation event used to purge data when reset_userdata is called.
33+
* @see \cache_helper::purge_by_event()
34+
**/
35+
public const INVALIDATION_RESET_USERDATA = 'resetuserdata';
36+
37+
/** @var ?override_cache The singleton instance for this class. */
38+
private static $instance = null;
3039

3140
/**
32-
* Create override_cache object and link to quiz
41+
* Returns the singleton instance of this class.
3342
*
34-
* @param int $quizid The quiz to link this cache to
43+
* @param definition $definition The cache definition.
44+
* @return override_cache The singleton instance.
3545
*/
36-
public function __construct(
37-
/** @var int $quizid ID of quiz cache is being operated on **/
38-
protected readonly int $quizid
39-
) {
46+
public static function get_instance_for_cache(definition $definition): override_cache {
47+
return self::$instance ??= new override_cache();
4048
}
4149

4250
/**
43-
* Returns the override cache
44-
*
45-
* @return \cache
51+
* {@inheritdoc}
52+
* @see \core_cache\data_source_interface::load_for_cache()
53+
*/
54+
public function load_for_cache($key) {
55+
global $DB;
56+
57+
if ($key === 'lastinvalidation') {
58+
return false;
59+
}
60+
61+
[$quizid, $userid] = self::split_cache_key($key);
62+
63+
$subquery = "SELECT g.id
64+
FROM {groups} g
65+
JOIN {groups_members} gm ON gm.groupid = g.id
66+
JOIN {quiz} q ON q.course = g.courseid
67+
WHERE q.id = :subqueryquizid AND gm.userid = :subqueryuserid";
68+
69+
$sql = "SELECT *
70+
FROM {quiz_overrides}
71+
WHERE quiz = :quizid AND (userid = :userid OR groupid IN ($subquery))";
72+
73+
$records = $DB->get_records_sql($sql, [
74+
'quizid' => $quizid,
75+
'userid' => $userid,
76+
'subqueryquizid' => $quizid,
77+
'subqueryuserid' => $userid,
78+
]);
79+
80+
return empty($records) ? null : $records;
81+
}
82+
83+
/**
84+
* {@inheritdoc}
85+
* @see \core_cache\data_source_interface::load_many_for_cache()
4686
*/
47-
protected function get_cache(): \cache {
48-
return \cache::make('mod_quiz', 'overrides');
87+
public function load_many_for_cache(array $keys) {
88+
$results = [];
89+
foreach ($keys as $key) {
90+
$results[$key] = $this->load_for_cache($key);
91+
}
92+
return $results;
4993
}
5094

5195
/**
52-
* Returns group cache key
96+
* Get all overrides for a given quiz and user.
5397
*
54-
* @param int $groupid
55-
* @return string the group cache key
98+
* @param int $quizid The quiz id.
99+
* @param int $userid The user id.
100+
* @return ?array Array of overrides or null if none found.
56101
*/
57-
protected function get_group_cache_key(int $groupid): string {
58-
return "{$this->quizid}_g_{$groupid}";
102+
public static function get_overrides(int $quizid, int $userid): array|null {
103+
$cache = self::get_cache();
104+
$key = self::get_cache_key($quizid, $userid);
105+
return $cache->get($key);
59106
}
60107

61108
/**
62-
* Returns user cache key
63-
*
64-
* @param int $userid
65-
* @return string the user cache key
109+
* Purge all overrides from the cache.
66110
*/
67-
protected function get_user_cache_key(int $userid): string {
68-
return "{$this->quizid}_u_{$userid}";
111+
public static function purge_all(): void {
112+
self::get_cache()->purge();
69113
}
70114

71115
/**
72-
* Returns the override value in the cache for the given group
116+
* Purge overrides for a specific user in a specific quiz.
73117
*
74-
* @param int $groupid group to get cached override data for
75-
* @return ?\stdClass override value in the cache for the given group, or null if there is none.
118+
* @param int $quizid The quiz id.
119+
* @param int $userid The user id.
76120
*/
77-
public function get_cached_group_override(int $groupid): ?\stdClass {
78-
$raw = $this->get_cache()->get($this->get_group_cache_key($groupid));
79-
return empty($raw) || !is_object($raw) ? null : (object) $raw;
121+
public static function purge_for_user(int $quizid, int $userid): void {
122+
self::purge_for_users($quizid, [$userid]);
80123
}
81124

82125
/**
83-
* Returns the override value in the cache for the given user
126+
* Purge overrides for specific users in a specific quiz.
84127
*
85-
* @param int $userid user to get cached override data for
86-
* @return ?\stdClass the override value in the cache for the given user, or null if there is none.
128+
* @param int $quizid The quiz id.
129+
* @param int[] $userids The user ids.
87130
*/
88-
public function get_cached_user_override(int $userid): ?\stdClass {
89-
$raw = $this->get_cache()->get($this->get_user_cache_key($userid));
90-
return empty($raw) || !is_object($raw) ? null : (object) $raw;
131+
public static function purge_for_users(int $quizid, array $userids): void {
132+
if (empty($userids)) {
133+
return;
134+
}
135+
136+
$keys = array_map(fn($userid): string => self::get_cache_key($quizid, $userid), $userids);
137+
$cache = self::get_cache();
138+
$cache->delete_many($keys);
91139
}
92140

93141
/**
94-
* Deletes the cached override data for a given group
142+
* Get the cache instance.
95143
*
96-
* @param int $groupid group to delete data for
144+
* @return cache The cache instance.
97145
*/
98-
public function clear_for_group(int $groupid): void {
99-
$this->get_cache()->delete($this->get_group_cache_key($groupid));
146+
private static function get_cache(): cache {
147+
return cache::make('mod_quiz', 'overrides');
100148
}
101149

102150
/**
103-
* Deletes the cached override data for the given user
151+
* Generate a cache key for a given quiz and user.
104152
*
105-
* @param int $userid user to delete data for
153+
* @param int $quizid The quiz id.
154+
* @param int $userid The user id.
155+
* @return string The cache key.
106156
*/
107-
public function clear_for_user(int $userid): void {
108-
$this->get_cache()->delete($this->get_user_cache_key($userid));
157+
private static function get_cache_key(int $quizid, int $userid): string {
158+
return "{$quizid}_{$userid}";
109159
}
110160

111161
/**
112-
* Clears the cache for the given user and/or group.
162+
* Split a cache key into its quizid and userid components.
113163
*
114-
* @param ?int $userid user to delete data for, or null.
115-
* @param ?int $groupid group to delete data for, or null.
164+
* @param string $key The cache key.
165+
* @return array An array with quizid and userid.
116166
*/
117-
public function clear_for(?int $userid = null, ?int $groupid = null): void {
118-
if (!empty($userid)) {
119-
$this->clear_for_user($userid);
120-
}
121-
122-
if (!empty($groupid)) {
123-
$this->clear_for_group($groupid);
124-
}
167+
private static function split_cache_key(string $key): array {
168+
return array_map('intval', explode('_', $key));
125169
}
126170
}

0 commit comments

Comments
 (0)