From 7da679abe09acdd4d2f88711738d223c62b150ab Mon Sep 17 00:00:00 2001 From: Stephan Robotta Date: Wed, 4 Dec 2024 16:13:25 +0100 Subject: [PATCH] Add index to local string table to load all strings for one instance at once. --- classes/repository/instance_repository.php | 71 ++++++++++++--- .../repository/model/db_localized_string.php | 15 +++- db/upgrade.php | 89 +++++++++++++++++++ version.php | 2 +- 4 files changed, 165 insertions(+), 12 deletions(-) diff --git a/classes/repository/instance_repository.php b/classes/repository/instance_repository.php index 51a13ea..06a2356 100644 --- a/classes/repository/instance_repository.php +++ b/classes/repository/instance_repository.php @@ -70,7 +70,7 @@ public static function get_by_id(int $id): instance { $category = db_instance_category::to_instance_category($dbocategory); // Load category headers. - $dbolocalizedstrings = self::get_strings(localized_string_type::INSTANCE_CATEGORY_HEADER, $category->get_id()); + $dbolocalizedstrings = self::get_strings(localized_string_type::INSTANCE_CATEGORY_HEADER, $category->get_id(), $id); foreach ($dbolocalizedstrings as $dbo) { $header = db_localized_string::to_localized_string($dbo); $category->add_header($header); @@ -81,7 +81,7 @@ public static function get_by_id(int $id): instance { foreach ($criteria as $criterion) { // Load criterion description. - $dbodescriptions = self::get_strings(localized_string_type::INSTANCE_CRITERION, $criterion->get_id()); + $dbodescriptions = self::get_strings(localized_string_type::INSTANCE_CRITERION, $criterion->get_id(), $id); foreach ($dbodescriptions as $dbodescription) { $description = db_localized_string::to_localized_string($dbodescription); $criterion->add_description($description); @@ -104,7 +104,7 @@ public static function get_by_id(int $id): instance { // Load subrating very positive texts. localized_string_type::INSTANCE_SUBRATING_VERY_POSITIVE => 'verypositives', ] as $type => $attribute) { - $dbotitles = self::get_strings($type, $subrating->get_id()); + $dbotitles = self::get_strings($type, $subrating->get_id(), $id); foreach ($dbotitles as $dbotitle) { $item = db_localized_string::to_localized_string($dbotitle); $subrating->{$attribute}[] = $item; @@ -113,7 +113,7 @@ public static function get_by_id(int $id): instance { // Load subrating descriptions. $dbosubratingdescriptions = self::get_strings(localized_string_type::INSTANCE_SUBRATING_DESCRIPTION, - $subrating->get_id()); + $subrating->get_id(), $id); foreach ($dbosubratingdescriptions as $dbosubratingdescription) { $subratingdescription = db_localized_string::to_localized_string($dbosubratingdescription); $subrating->add_description($subratingdescription); @@ -121,7 +121,7 @@ public static function get_by_id(int $id): instance { // Load subrating very negative texts. $dboverynegatives = self::get_strings(localized_string_type::INSTANCE_SUBRATING_VERY_NEGATIVE, - $subrating->get_id()); + $subrating->get_id(), $id); foreach ($dboverynegatives as $dboverynegative) { $verynegative = db_localized_string::to_localized_string($dboverynegative); $subrating->add_verynegative($verynegative); @@ -129,7 +129,7 @@ public static function get_by_id(int $id): instance { // Load subrating negative texts. $dbonegatives = self::get_strings(localized_string_type::INSTANCE_SUBRATING_NEGATIVE, - $subrating->get_id()); + $subrating->get_id(), $id); foreach ($dbonegatives as $dbonegative) { $negative = db_localized_string::to_localized_string($dbonegative); $subrating->add_negative($negative); @@ -137,7 +137,7 @@ public static function get_by_id(int $id): instance { // Load subrating positive texts. $dbopositives = self::get_strings(localized_string_type::INSTANCE_SUBRATING_POSITIVE, - $subrating->get_id()); + $subrating->get_id(), $id); foreach ($dbopositives as $dbopositive) { $positive = db_localized_string::to_localized_string($dbopositive); $subrating->add_positive($positive); @@ -145,7 +145,7 @@ public static function get_by_id(int $id): instance { // Load subrating very positive texts. $dboverypositives = self::get_strings(localized_string_type::INSTANCE_SUBRATING_VERY_POSITIVE, - $subrating->get_id()); + $subrating->get_id(), $id); foreach ($dboverypositives as $dboverypositive) { $verypositive = db_localized_string::to_localized_string($dboverypositive); $subrating->add_verypositive($verypositive); @@ -167,22 +167,35 @@ public static function get_by_id(int $id): instance { * * @param string $type * @param int $subratingid + * @param int $instanceid * @param bool $throwonerror * @return array * @throws \coding_exception * @throws \dml_exception */ - private static function get_strings(string $type, int $subratingid, bool $throwonerror = false): array { + private static function get_strings(string $type, int $subratingid, int $instanceid = 0, bool $throwonerror = false): array { global $DB; static $sortedstrings = []; + $typeid = localized_string_type::str2id($type); + + if ($instanceid > 0) { + $strings = self::get_strings_for_instance($instanceid); + if (array_key_exists($typeid, $strings) && array_key_exists($subratingid, $strings[$typeid])) { + return $strings[$typeid][$subratingid]; + } + if ($throwonerror) { + throw new \coding_exception("No strings for type $type and subratingid $subratingid"); + } + return []; + } $cachekey = $type . '~~' . $subratingid; if (!array_key_exists($cachekey, $sortedstrings) || PHPUNIT_TEST) { $sortedstrings[$cachekey] = []; $rs = $DB->get_recordset( tables::LOCALIZED_STRING_TABLE, - ['typeid' => localized_string_type::str2id($type), 'foreignkey' => $subratingid] + ['typeid' => $typeid, 'foreignkey' => $subratingid] ); foreach ($rs as $dboheader) { $dbobj = new db_localized_string; @@ -207,6 +220,44 @@ private static function get_strings(string $type, int $subratingid, bool $throwo return $sortedstrings[$cachekey]; } + /** + * Same as get_strings but for a specific instance, load the string all at once. + * + * @param int $id - instance id of the verbal feedback activity. + * @return array + */ + private static function get_strings_for_instance(int $id): array { + global $DB; + + static $byinstance = []; + + if (!array_key_exists($id, $byinstance) || PHPUNIT_TEST) { + $byinstance[$id] = []; + $rs = $DB->get_recordset(tables::LOCALIZED_STRING_TABLE, ['instanceid' => $id]); + foreach ($rs as $dboheader) { + $dbobj = new db_localized_string; + $dbobj->id = $dboheader->id; + $dbobj->languageid = $dboheader->languageid; + $dbobj->string = $dboheader->string; + $dbobj->typeid = $dboheader->typeid; + $dbobj->foreignkey = $dboheader->foreignkey; + $dbobj->instanceid = $dboheader->instanceid; + + if (!array_key_exists($dbobj->typeid, $byinstance[$id])) { + $byinstance[$id][$dbobj->typeid] = []; + } + if (!array_key_exists($dbobj->foreignkey, $byinstance[$id][$dbobj->typeid])) { + $byinstance[$id][$dbobj->typeid][$dbobj->foreignkey] = []; + } + // Store the localized string by instanceid, typeid, foreignkey, languageid. + $byinstance[$id][$dbobj->typeid][$dbobj->foreignkey][$dbobj->languageid] = $dbobj; + } + $rs->close(); + } + + return $byinstance[$id]; + } + /** * Gets all criteria hashed by category id for a specific instance. * @param int $id - instance id diff --git a/classes/repository/model/db_localized_string.php b/classes/repository/model/db_localized_string.php index f3c14dc..b7ca0c1 100644 --- a/classes/repository/model/db_localized_string.php +++ b/classes/repository/model/db_localized_string.php @@ -29,38 +29,50 @@ * The database localized string class */ class db_localized_string { + /** * @var int The id */ public $id; + /** * @var int The type */ public $typeid; + /** * @var int The foreign key */ + public $foreignkey; + /** * @var string The language id */ public $languageid; + /** * @var string The string */ public $string; + /** + * @var int The instance id of the verbal feedback activity + */ + public $instanceid; + /** * Return a localized string database object * * @param localized_string $localizedstring The localized string * @param string $type The string type * @param int $foreignkey The foreign key + * @param int $instanceid The instance id * @return db_localized_string * @throws \Exception */ public static function from_localized_string(localized_string $localizedstring, string $type, - int $foreignkey): db_localized_string { + int $foreignkey, int $instanceid): db_localized_string { if (!localized_string_type::exists($type)) { throw new \Exception("unknown localized_string_type"); } @@ -70,6 +82,7 @@ public static function from_localized_string(localized_string $localizedstring, $dbo->foreignkey = $foreignkey; $dbo->languageid = $localizedstring->get_language_id(); $dbo->string = $localizedstring->get_string(); + $dbo->instanceid = $instanceid; return $dbo; } diff --git a/db/upgrade.php b/db/upgrade.php index ad85f43..85770bf 100644 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -42,6 +42,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +use mod_verbalfeedback\repository\tables; use mod_verbalfeedback\repository\model\localized_string_type; /** @@ -105,5 +106,93 @@ function xmldb_verbalfeedback_upgrade($oldversion) { upgrade_mod_savepoint(true, 2024101700, 'verbalfeedback'); } + if ($oldversion < 2024120400) { + // Add instance id to localized string table and put an index on it so that when loading + // the strings, we can load them all at once by instance id of the verbal feedback activity. + $table = new xmldb_table('verbalfeedback_local_string'); + $field = new xmldb_field('instanceid', XMLDB_TYPE_INTEGER, '3', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 0); + + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + add_instance_to_localized_string(); + $table->add_index('instanceidx', XMLDB_INDEX_NOTUNIQUE, ['instanceid']); + + upgrade_mod_savepoint(true, 2024120400, 'verbalfeedback'); + } + return true; } + + +/** + * Go over all existing strings in the database and add instance id of the verbal feedback activity to the localized string. + */ +function add_instance_to_localized_string() { + global $DB; + + // Category header. + $sql = sprintf(' + UPDATE {%2$s} SET instanceid = {%1$s}.instanceid FROM {%1$s} + WHERE {%1$s}.id = {%2$s}.foreignkey AND {%2$s}.typeid IN (?, ?)', + tables::INSTANCE_CATEGORY_TABLE, + tables::LOCALIZED_STRING_TABLE, + ); + $DB->execute($sql, [ + localized_string_type::str2id(localized_string_type::INSTANCE_CATEGORY_HEADER), + localized_string_type::str2id(localized_string_type::TEMPLATE_CATEGORY_HEADER), + ]); + + // Category criterion. + $sql = sprintf(' + SELECT {%1$s}.id, {%2$s}.instanceid FROM {%1$s} JOIN {%2$s} ON {%2$s}.id = {%1$s}.categoryid + WHERE {%2$s}.id IN (SELECT DISTINCT(id) FROM {%2$s}) + ', + tables::INSTANCE_CRITERION_TABLE, + tables::INSTANCE_CATEGORY_TABLE + ); + $results = $DB->get_records_sql($sql); + foreach ($results as $result) { + $DB->execute( + sprintf('UPDATE {%s} SET instanceid = ? + WHERE foreignkey = ? AND typeid IN (?, ?)', tables::LOCALIZED_STRING_TABLE), + [ + $result->instanceid, + $result->id, + localized_string_type::str2id(localized_string_type::INSTANCE_CRITERION), + localized_string_type::str2id(localized_string_type::TEMPLATE_CRITERION), + ] + ); + } + + // Subcriteria. + $sql = sprintf(' + SELECT {%1$s}.id, {%3$s}.instanceid + FROM {%1$s} + JOIN {%2$s} ON {%1$s}.criterionid = {%2$s}.id + JOIN {%3$s} ON {%2$s}.categoryid = {%3$s}.id + ', tables::INSTANCE_SUBRATING_TABLE, tables::INSTANCE_CRITERION_TABLE, tables::INSTANCE_CATEGORY_TABLE); + $results = $DB->get_records_sql($sql); + foreach ($results as $result) { + $DB->execute( + sprintf('UPDATE {%s} SET instanceid = ? + WHERE foreignkey = ? AND typeid IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', tables::LOCALIZED_STRING_TABLE), + [ + $result->instanceid, + $result->id, + localized_string_type::str2id(localized_string_type::INSTANCE_SUBRATING_TITLE), + localized_string_type::str2id(localized_string_type::INSTANCE_SUBRATING_DESCRIPTION), + localized_string_type::str2id(localized_string_type::INSTANCE_SUBRATING_VERY_NEGATIVE), + localized_string_type::str2id(localized_string_type::INSTANCE_SUBRATING_NEGATIVE), + localized_string_type::str2id(localized_string_type::INSTANCE_SUBRATING_POSITIVE), + localized_string_type::str2id(localized_string_type::INSTANCE_SUBRATING_VERY_POSITIVE), + localized_string_type::str2id(localized_string_type::TEMPLATE_SUBRATING_DESCRIPTION), + localized_string_type::str2id(localized_string_type::TEMPLATE_SUBRATING_VERY_NEGATIVE), + localized_string_type::str2id(localized_string_type::TEMPLATE_SUBRATING_NEGATIVE), + localized_string_type::str2id(localized_string_type::TEMPLATE_SUBRATING_POSITIVE), + localized_string_type::str2id(localized_string_type::TEMPLATE_SUBRATING_VERY_POSITIVE), + ] + ); + } +} \ No newline at end of file diff --git a/version.php b/version.php index 0408482..60663b5 100644 --- a/version.php +++ b/version.php @@ -25,7 +25,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'mod_verbalfeedback'; -$plugin->version = 2024101700; +$plugin->version = 2024120400; $plugin->requires = 2022112800; $plugin->maturity = MATURITY_STABLE; $plugin->cron = 0;