From a30570c5550970f71aaef918a4e904edc3ff38ae Mon Sep 17 00:00:00 2001 From: Ryan Wyllie Date: Fri, 8 Mar 2019 14:44:10 +0800 Subject: [PATCH] MDL-64820 dml: add object preloading helpers --- lib/dml/moodle_database.php | 59 ++++++++++++++++ lib/dml/tests/dml_test.php | 130 ++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+) diff --git a/lib/dml/moodle_database.php b/lib/dml/moodle_database.php index 9e781cd8952..72d78b2ba65 100644 --- a/lib/dml/moodle_database.php +++ b/lib/dml/moodle_database.php @@ -828,6 +828,65 @@ abstract class moodle_database { return array($sql, $params); } + /** + * Get the SELECT SQL to preload columns for the specified fieldlist and table alias. + * + * This function is intended to be used in combination with get_preload_columns_sql and extract_from_fields. + * + * @param array $fieldlist The list of fields from get_preload_columns + * @param string $tablealias The table alias used in the FROM/JOIN field + * @return string The SQL to use in the SELECT + */ + public function get_preload_columns_sql(array $fieldlist, string $tablealias) : string { + return implode(', ', array_map(function($fieldname, $alias) use ($tablealias) { + return "{$tablealias}.{$fieldname} AS {$alias}"; + }, $fieldlist, array_keys($fieldlist))); + } + + /** + * Extract fields from the specified data. + * The fields are removed from the original object. + * + * This function is intended to be used in combination with get_preload_columns and get_preload_columns_sql. + * + * @param array $fieldlist The list of fields from get_preload_columns + * @param \stdClass $data The data retrieved from the database with fields to be extracted + * @return string The SQL to use in the SELECT + */ + public function extract_fields_from_object(array $fieldlist, \stdClass $data) : \stdClass { + $newdata = (object) []; + foreach ($fieldlist as $alias => $fieldname) { + if (property_exists($data, $alias)) { + $newdata->$fieldname = $data->$alias; + unset($data->$alias); + } else { + debugging("Field '{$fieldname}' not found", DEBUG_DEVELOPER); + } + } + + return $newdata; + } + + /** + * Get the preload columns for the specified table and use the specified prefix in the column alias. + * + * This function is intended to be used in combination with get_preload_columns_sql and extract_from_fields. + * + * @param string $table + * @param string $prefix + * @return array The list of columns in a table. The array key is the column name with an applied prefix. + */ + public function get_preload_columns(string $table, string $prefix) : array { + global $DB; + + $fields = []; + foreach (array_keys($this->get_columns($table)) as $fieldname) { + $fields["{$prefix}{$fieldname}"] = $fieldname; + } + + return $fields; + } + /** * Converts short table name {tablename} to the real prefixed table name in given sql. * @param string $sql The sql to be operated on. diff --git a/lib/dml/tests/dml_test.php b/lib/dml/tests/dml_test.php index fb144e9c736..f89378aa8a2 100644 --- a/lib/dml/tests/dml_test.php +++ b/lib/dml/tests/dml_test.php @@ -794,6 +794,136 @@ class core_dml_testcase extends database_driver_testcase { $this->assertFalse($columns['id']->auto_increment); } + public function test_get_preload_columns() { + $DB = $this->tdb; + $dbman = $this->tdb->get_manager(); + + $table = $this->get_test_table(); + $tablename = $table->getName(); + + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('course', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, 'lala'); + $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); + $dbman->create_table($table); + + $expected = [ + 'aid' => 'id', + 'acourse' => 'course', + 'aname' => 'name', + ]; + $columns = $DB->get_preload_columns($tablename, 'a'); + $this->assertCount(3, $columns); + $this->assertEquals($expected, $columns); + } + + /** + * Ensure that get_preload_columns_sql works as expected. + * + * @dataProvider get_preload_columns_sql_provider + * @param array $fieldlist The list of fields + * @param string $tablealias The alias to use + * @param string $expected The string to match + */ + public function test_get_preload_columns_sql(array $fieldlist, string $tablealias, string $expected) { + $this->assertEquals($expected, $this->tdb->get_preload_columns_sql($fieldlist, $tablealias)); + } + + /** + * Data provider for get_preload_columns_sql tests. + * + * @return array + */ + public function get_preload_columns_sql_provider() : array { + return [ + 'single field' => [ + [ + 'xid' => 'id', + ], + 'x', + 'x.id AS xid', + ], + 'multiple fields' => [ + [ + 'bananaid' => 'id', + 'bananacourse' => 'course', + 'bananafoo' => 'foo', + ], + 'banana', + 'banana.id AS bananaid, banana.course AS bananacourse, banana.foo AS bananafoo', + ], + ]; + } + + /** + * Ensure that extract_fields_from_object works as expected. + * + * @dataProvider extract_fields_from_object_provider + * @param array $fieldlist The list of fields + * @param stdClass $in Input values for the test + * @param stdClass $out The expected output + * @param stdClass $modified Expected value of $in after it's been modified + */ + public function test_extract_fields_from_object(array $fieldlist, \stdClass $in, \stdClass $out, \stdClass $modified) { + $result = $this->tdb->extract_fields_from_object($fieldlist, $in); + $this->assertEquals($out, $result); + $this->assertEquals($modified, $in); + } + + /** + * Data provider for extract_fields_from_object tests. + * + * @return array + */ + public function extract_fields_from_object_provider() : array { + return [ + 'single table' => [ + [ + 'sid' => 'id', + 'scourse' => 'course', + 'sflag' => 'flag', + ], + (object) [ + 'sid' => 1, + 'scourse' => 42, + 'sflag' => 'foo', + ], + (object) [ + 'id' => 1, + 'course' => 42, + 'flag' => 'foo', + ], + (object) [ + ], + ], + 'single table amongst others' => [ + [ + 'sid' => 'id', + 'scourse' => 'course', + 'sflag' => 'flag', + ], + (object) [ + 'sid' => 1, + 'scourse' => 42, + 'sflag' => 'foo', + 'oid' => 'id', + 'ocourse' => 'course', + 'oflag' => 'flag', + ], + (object) [ + 'id' => 1, + 'course' => 42, + 'flag' => 'foo', + ], + (object) [ + 'oid' => 'id', + 'ocourse' => 'course', + 'oflag' => 'flag', + ], + ], + ]; + } + public function test_get_manager() { $DB = $this->tdb; $dbman = $this->tdb->get_manager();