From 3bb15852700b19c417fd619416455f06fcd27331 Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Tue, 13 Oct 2015 16:28:33 +0800 Subject: [PATCH] MDL-49231 mod_glossary: Use a query builder to avoid code duplication --- mod/glossary/classes/entry_query_builder.php | 438 +++++++++++++++++++ mod/glossary/classes/external.php | 243 ++++------ 2 files changed, 521 insertions(+), 160 deletions(-) create mode 100644 mod/glossary/classes/entry_query_builder.php diff --git a/mod/glossary/classes/entry_query_builder.php b/mod/glossary/classes/entry_query_builder.php new file mode 100644 index 00000000000..7c6baad0bb9 --- /dev/null +++ b/mod/glossary/classes/entry_query_builder.php @@ -0,0 +1,438 @@ +. + +/** + * Entry query builder. + * + * @package mod_glossary + * @copyright 2015 Frédéric Massart - FMCorz.net + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Entry query builder class. + * + * The purpose of this class is to avoid duplicating SQL statements to fetch entries + * which are very similar with each other. This builder is not meant to be smart, it + * will not out rule any previously set condition, or join, etc... + * + * You should be using this builder just like you would be creating your SQL query. Only + * some methods are shorthands to avoid logic duplication and common mistakes. + * + * @package mod_glossary + * @copyright 2015 Frédéric Massart - FMCorz.net + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class mod_glossary_entry_query_builder { + + /** Alias for table glossary_categories. */ + const ALIAS_CATEGORIES = 'gc'; + /** Alias for table glossary_entries_categories. */ + const ALIAS_ENTRIES_CATEGORIES = 'gec'; + /** Alias for table glossary_entries. */ + const ALIAS_ENTRIES = 'ge'; + /** Alias for table user. */ + const ALIAS_USER = 'u'; + + /** Include none of the entries to approve. */ + const NON_APPROVED_NONE = 'na_none'; + /** Including all the entries. */ + const NON_APPROVED_ALL = 'na_all'; + /** Including only the entries to be approved. */ + const NON_APPROVED_ONLY = 'na_only'; + /** Including my entries to be approved. */ + const NON_APPROVED_SELF = 'na_self'; + + /** @var array Raw SQL statements representing the fields to select. */ + protected $fields = array(); + /** @var array Raw SQL statements representing the JOINs to make. */ + protected $joins = array(); + /** @var string Raw SQL statement representing the FROM clause. */ + protected $from; + /** @var object The glossary we are fetching from. */ + protected $glossary; + /** @var int The number of records to fetch from. */ + protected $limitfrom = 0; + /** @var int The number of records to fetch. */ + protected $limitnum = 0; + /** @var array List of SQL parameters. */ + protected $params = array(); + /** @var array Raw SQL statements representing the ORDER clause. */ + protected $order = array(); + /** @var array Raw SQL statements representing the WHERE clause. */ + protected $where = array(); + + /** + * Constructor. + * + * @param object $glossary The glossary. + */ + public function __construct($glossary) { + $this->glossary = $glossary; + $this->from = sprintf('FROM {glossary_entries} %s', self::ALIAS_ENTRIES); + $this->where[] = sprintf('(%s.glossaryid = :gid OR %s.sourceglossaryid = :gid2)', self::ALIAS_ENTRIES, self::ALIAS_ENTRIES); + $this->params['gid'] = $glossary->id; + $this->params['gid2'] = $glossary->id; + } + + /** + * Add a field to select. + * + * @param string $field The field, or *. + * @param string $table The table name, without the prefix 'glossary_'. + * @param string $alias An alias for the field. + */ + public function add_field($field, $table, $alias = null) { + $field = self::resolve_field($field, $table); + if (!empty($alias)) { + $field .= ' AS ' . $alias; + } + $this->fields[] = $field; + } + + /** + * Adds the user fields. + * + * @return void + */ + public function add_user_fields() { + $this->fields[] = user_picture::fields('u', null, 'userdataid', 'userdata'); + } + + /** + * Internal method to build the query. + * + * @param boolean $count Query to count? + * @return string The SQL statement. + */ + protected function build_query($count = false) { + $sql = 'SELECT '; + + if ($count) { + $sql .= 'COUNT(\'x\') '; + } else { + $sql .= implode(', ', $this->fields) . ' '; + } + + $sql .= $this->from . ' '; + $sql .= implode(' ', $this->joins) . ' '; + + if (!empty($this->where)) { + $sql .= 'WHERE (' . implode(') AND (', $this->where) . ') '; + } + + if (!$count && !empty($this->order)) { + $sql .= 'ORDER BY ' . implode(', ', $this->order); + } + + return $sql; + } + + /** + * Count the records. + * + * @return int + */ + public function count_records() { + global $DB; + return $DB->count_records_sql($this->build_query(true), $this->params); + } + + /** + * Filter a field using a letter. + * + * @param string $letter The letter. + * @param string $finalfield The SQL statement representing the field. + */ + protected function filter_by_letter($letter, $finalfield) { + global $DB; + + $letter = core_text::strtoupper($letter); + $len = core_text::strlen($letter); + $sql = $DB->sql_substr(sprintf('upper(%s)', $finalfield), 1, $len); + + $this->where[] = "$sql = :letter"; + $this->params['letter'] = $letter; + } + + /** + * Filter a field by special characters. + * + * @param string $finalfield The SQL statement representing the field. + */ + protected function filter_by_non_letter($finalfield) { + global $DB; + + $alphabet = explode(',', get_string('alphabet', 'langconfig')); + list($nia, $aparams) = $DB->get_in_or_equal($alphabet, SQL_PARAMS_NAMED, 'nonletter', false); + + $sql = $DB->sql_substr(sprintf('upper(%s)', $finalfield), 1, 1); + + $this->where[] = "$sql $nia"; + $this->params = array_merge($this->params, $aparams); + } + + /** + * Filter the author by letter. + * + * @param string $letter The letter. + * @param boolean $firstnamefirst Whether or not the firstname is first in the author's name. + */ + public function filter_by_author_letter($letter, $firstnamefirst = false) { + $field = self::get_fullname_field($firstnamefirst); + $this->filter_by_letter($letter, $field); + } + + /** + * Filter the author by special characters. + * + * @param boolean $firstnamefirst Whether or not the firstname is first in the author's name. + */ + public function filter_by_author_non_letter($firstnamefirst = false) { + $field = self::get_fullname_field($firstnamefirst); + $this->filter_by_non_letter($field); + } + + /** + * Filter the concept by letter. + * + * @param string $letter The letter. + */ + public function filter_by_concept_letter($letter) { + $this->filter_by_letter($letter, self::resolve_field('concept', 'entries')); + } + + /** + * Filter the concept by special characters. + * + * @return void + */ + public function filter_by_concept_non_letter() { + $this->filter_by_non_letter(self::resolve_field('concept', 'entries')); + } + + /** + * Filter non approved entries. + * + * @param string $constant One of the NON_APPROVED_* constants. + * @param int $userid The user ID when relevant, otherwise current user. + */ + public function filter_by_non_approved($constant, $userid = null) { + global $USER; + if (!$userid) { + $userid = $USER->id; + } + + if ($constant === self::NON_APPROVED_ALL) { + // Nothing to do. + + } else if ($constant === self::NON_APPROVED_SELF) { + $this->where[] = sprintf('%s != 0 OR %s = :toapproveuserid', + self::resolve_field('approved', 'entries'), self::resolve_field('userid', 'entries')); + $this->params['toapproveuserid'] = $USER->id; + + } else if ($constant === self::NON_APPROVED_NONE) { + $this->where[] = sprintf('%s != 0', self::resolve_field('approved', 'entries')); + + } else if ($constant === self::NON_APPROVED_ONLY) { + $this->where[] = sprintf('%s = 0', self::resolve_field('approved', 'entries')); + + } else { + throw new coding_exception('Invalid constant'); + } + } + + /** + * Convenience method to get get the SQL statement for the full name. + * + * @param boolean $firstnamefirst Whether or not the firstname is first in the author's name. + * @return string The SQL statement. + */ + public static function get_fullname_field($firstnamefirst = false) { + global $DB; + if ($firstnamefirst) { + return $DB->sql_fullname(self::resolve_field('firstname', 'user'), self::resolve_field('lastname', 'user')); + } + return $DB->sql_fullname(self::resolve_field('lastname', 'user'), self::resolve_field('firstname', 'user')); + } + + /** + * Get the records. + * + * @return array + */ + public function get_records() { + global $DB; + return $DB->get_records_sql($this->build_query(), $this->params, $this->limitfrom, $this->limitnum); + } + + /** + * Get the recordset. + * + * @return moodle_recordset + */ + public function get_recordset() { + global $DB; + return $DB->get_recordset_sql($this->build_query(), $this->params, $this->limitfrom, $this->limitnum); + } + + /** + * Retrieve a user object from a record. + * + * This comes handy when {@link self::add_user_fields} was used. + * + * @param stdClass $record The record. + * @return stdClass A user object. + */ + public static function get_user_from_record($record) { + return user_picture::unalias($record, null, 'userdataid', 'userdata'); + } + + /** + * Join on the category tables. + * + * Depending on the category passed the joins will be different. This is due to the display + * logic that assumes that when displaying all categories the non categorised entries should + * not be returned, etc... + * + * @param int $categoryid The category ID, or GLOSSARY_SHOW_* constant. + */ + public function join_category($categoryid) { + + if ($categoryid === GLOSSARY_SHOW_ALL_CATEGORIES) { + $this->joins[] = sprintf('JOIN {glossary_entries_categories} %s ON %s = %s', + self::ALIAS_ENTRIES_CATEGORIES, self::resolve_field('id', 'entries'), + self::resolve_field('entryid', 'entries_categories')); + + $this->joins[] = sprintf('JOIN {glossary_categories} %s ON %s = %s', + self::ALIAS_CATEGORIES, self::resolve_field('id', 'categories'), + self::resolve_field('categoryid', 'entries_categories')); + + } else if ($categoryid === GLOSSARY_SHOW_NOT_CATEGORISED) { + $this->joins[] = sprintf('LEFT JOIN {glossary_entries_categories} %s ON %s = %s', + self::ALIAS_ENTRIES_CATEGORIES, self::resolve_field('id', 'entries'), + self::resolve_field('entryid', 'entries_categories')); + + } else { + $this->joins[] = sprintf('JOIN {glossary_entries_categories} %s ON %s = %s AND %s = :joincategoryid', + self::ALIAS_ENTRIES_CATEGORIES, self::resolve_field('id', 'entries'), + self::resolve_field('entryid', 'entries_categories'), + self::resolve_field('categoryid', 'entries_categories')); + $this->params['joincategoryid'] = $categoryid; + + } + } + + /** + * Join the user table. + * + * @param boolean $strict When strict uses a JOIN rather than a LEFT JOIN. + */ + public function join_user($strict = false) { + $join = $strict ? 'JOIN' : 'LEFT JOIN'; + $this->joins[] = sprintf("$join {user} %s ON %s = %s", + self::ALIAS_USER, self::resolve_field('id', 'user'), self::resolve_field('userid', 'entries')); + } + + /** + * Limit the number of records to fetch. + * @param int $from Fetch from. + * @param int $num Number to fetch. + */ + public function limit($from, $num) { + $this->limitfrom = $from; + $this->limitnum = $num; + } + + /** + * Normalise a direction. + * + * This ensures that the value is either ASC or DESC. + * + * @param string $direction The desired direction. + * @return string ASC or DESC. + */ + protected function normalize_direction($direction) { + $direction = core_text::strtoupper($direction); + if ($direction == 'DESC') { + return 'DESC'; + } + return 'ASC'; + } + + /** + * Order by a field. + * + * @param string $field The field, or *. + * @param string $table The table name, without the prefix 'glossary_'. + * @param string $direction ASC, or DESC. + */ + public function order_by($field, $table, $direction = '') { + $direction = self::normalize_direction($direction); + $this->order[] = self::resolve_field($field, $table) . ' ' . $direction; + } + + /** + * Order by author name. + * + * @param boolean $firstnamefirst Whether or not the firstname is first in the author's name. + * @param string $direction [description] + * @param string $direction ASC, or DESC. + */ + public function order_by_author($firstnamefirst = false, $direction = '') { + $field = self::get_fullname_field($firstnamefirst); + $direction = self::normalize_direction($direction); + $this->order[] = $field . ' ' . $direction; + } + + /** + * Convenience method to transform a field into SQL statement. + * + * @param string $field The field, or *. + * @param string $table The table name, without the prefix 'glossary_'. + * @return string SQL statement. + */ + protected static function resolve_field($field, $table) { + $prefix = constant(__CLASS__ . '::ALIAS_' . core_text::strtoupper($table)); + return sprintf('%s.%s', $prefix, $field); + } + + /** + * Simple where conditions. + * + * @param string $field The field, or *. + * @param string $table The table name, without the prefix 'glossary_'. + * @param mixed $value The value to be equal to. + */ + public function where($field, $table, $value) { + static $i = 0; + $sql = self::resolve_field($field, $table) . ' '; + + if ($value === null) { + $sql .= 'IS NULL'; + + } else { + $param = 'where' . $i++; + $sql .= " = :$param"; + $this->params[$param] = $value; + } + + $this->where[] = $sql; + } + +} diff --git a/mod/glossary/classes/external.php b/mod/glossary/classes/external.php index 27b58b72187..06e099a6a0f 100644 --- a/mod/glossary/classes/external.php +++ b/mod/glossary/classes/external.php @@ -125,7 +125,7 @@ class mod_glossary_external extends external_api { * @param context $context The context the entry belongs to. * @return void */ - public static function fill_entry_details($entry, $context, $useridfield = 'userdataid', $userfieldprefix = 'userdata') { + public static function fill_entry_details($entry, $context) { global $PAGE; $canviewfullnames = has_capability('moodle/site:viewfullnames', $context); @@ -135,8 +135,7 @@ class mod_glossary_external extends external_api { $context->id, 'mod_glossary', 'entry', $entry->id); // Author details. - $user = new stdclass(); - $user = user_picture::unalias($entry, null, $useridfield, $userfieldprefix); + $user = mod_glossary_entry_query_builder::get_user_from_record($entry); $userpicture = new user_picture($user); $userpicture->size = 1; $entry->userfullname = fullname($user, $canviewfullnames); @@ -485,43 +484,29 @@ class mod_glossary_external extends external_api { } // Preparing the query. - $where = '1 = 1'; - $params = array(); - - if ($letter != 'ALL' && $letter != 'SPECIAL' && ($letterstrlen = core_text::strlen($letter))) { - $params['hookup'] = core_text::strtoupper($letter); - $where = $DB->sql_substr('upper(concept)', 1, $letterstrlen) . ' = :hookup'; + $qb = new mod_glossary_entry_query_builder($glossary); + if ($letter != 'ALL' && $letter != 'SPECIAL' && core_text::strlen($letter)) { + $qb->filter_by_concept_letter($letter); } if ($letter == 'SPECIAL') { - $alphabet = explode(',', get_string('alphabet', 'langconfig')); - list($nia, $aparams) = $DB->get_in_or_equal($alphabet, SQL_PARAMS_NAMED, 'a', false); - $params = array_merge($params, $aparams); - $where = $DB->sql_substr("upper(concept)", 1, 1) . " $nia"; + $qb->filter_by_concept_non_letter(); } - $approvedsql = '(ge.approved <> 0 OR ge.userid = :myid)'; if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) { - $approvedsql = '1 = 1'; + $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL); + } else { + $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF); } - $userfields = user_picture::fields('u', null, 'userdataid', 'userdata'); - - $sqlselectcount = "SELECT COUNT('x')"; - $sqlselect = "SELECT ge.*, $userfields"; - $sql = " FROM {glossary_entries} ge - LEFT JOIN {user} u ON ge.userid = u.id - WHERE (ge.glossaryid = :gid1 OR ge.sourceglossaryid = :gid2) - AND $approvedsql - AND $where"; - $sqlorder = " ORDER BY ge.concept"; - - $params['gid1'] = $glossary->id; - $params['gid2'] = $glossary->id; - $params['myid'] = $USER->id; + $qb->add_field('*', 'entries'); + $qb->join_user(); + $qb->add_user_fields(); + $qb->order_by('concept', 'entries'); + $qb->limit($from, $limit); // Fetching the entries. - $count = $DB->count_records_sql($sqlselectcount . $sql, $params); - $entries = $DB->get_records_sql($sqlselect . $sql . $sqlorder, $params, $from, $limit); + $count = $qb->count_records(); + $entries = $qb->get_records(); foreach ($entries as $key => $entry) { self::fill_entry_details($entry, $context); } @@ -619,36 +604,27 @@ class mod_glossary_external extends external_api { } // Preparing the query. - $params = array(); - $approvedsql = '(ge.approved <> 0 OR ge.userid = :myid)'; + $qb = new mod_glossary_entry_query_builder($glossary); if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) { - $approvedsql = '1 = 1'; - } - - $userfields = user_picture::fields('u', null, 'userdataid', 'userdata'); - - $sqlselectcount = "SELECT COUNT('x')"; - $sqlselect = "SELECT ge.*, $userfields"; - $sql = " FROM {glossary_entries} ge - LEFT JOIN {user} u ON ge.userid = u.id - WHERE (ge.glossaryid = :gid1 OR ge.sourceglossaryid = :gid2) - AND $approvedsql"; - - $sqlorder = ' ORDER BY '; - if ($order == 'CREATION') { - $sqlorder .= 'ge.timecreated'; + $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL); } else { - $sqlorder .= 'ge.timemodified'; + $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF); } - $sqlorder .= ' ' . $sort; - $params['gid1'] = $glossary->id; - $params['gid2'] = $glossary->id; - $params['myid'] = $USER->id; + $qb->add_field('*', 'entries'); + $qb->join_user(); + $qb->add_user_fields(); + $qb->limit($from, $limit); + + if ($order == 'CREATION') { + $qb->order_by('timecreated', 'entries', $sort); + } else { + $qb->order_by('timemodified', 'entries', $sort); + } // Fetching the entries. - $count = $DB->count_records_sql($sqlselectcount . $sql, $params); - $entries = $DB->get_records_sql($sqlselect . $sql . $sqlorder, $params, $from, $limit); + $count = $qb->count_records(); + $entries = $qb->get_records(); foreach ($entries as $key => $entry) { self::fill_entry_details($entry, $context); } @@ -817,57 +793,33 @@ class mod_glossary_external extends external_api { } // Preparing the query. - $params = array(); - $glossarysql = '(ge.glossaryid = :gid1 OR ge.sourceglossaryid = :gid2)'; - $params['gid1'] = $glossary->id; - $params['gid2'] = $glossary->id; - - $approvedsql = '(ge.approved <> 0 OR ge.userid = :myid)'; - $params['myid'] = $USER->id; + $qb = new mod_glossary_entry_query_builder($glossary); if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) { - $approvedsql = '1 = 1'; + $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL); + } else { + $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF); } - $userfields = user_picture::fields('u', null, 'userdataid', 'userdata'); - $sqlfields = "ge.*, gec.categoryid, $userfields"; - $sqlorderfields = 'ge.concept'; + $qb->join_category($categoryid); + $qb->join_user(); + $qb->add_field('*', 'entries'); + $qb->add_field('categoryid', 'entries_categories'); + $qb->add_user_fields(); if ($categoryid === GLOSSARY_SHOW_ALL_CATEGORIES) { - $sqlfields .= ', gc.name AS categoryname'; - $sqlorderfields = 'gc.name, ge.concept'; - $sql = " FROM {glossary_entries} ge - JOIN {glossary_entries_categories} gec ON ge.id = gec.entryid - JOIN {glossary_categories} gc ON gc.id = gec.categoryid - LEFT JOIN {user} u ON ge.userid = u.id - WHERE $glossarysql - AND $approvedsql"; + $qb->add_field('name', 'categories', 'categoryname'); + $qb->order_by('name', 'categories'); } else if ($categoryid === GLOSSARY_SHOW_NOT_CATEGORISED) { - $sql = " FROM {glossary_entries} ge - LEFT JOIN {glossary_entries_categories} gec ON ge.id = gec.entryid - LEFT JOIN {user} u ON ge.userid = u.id - WHERE $glossarysql - AND $approvedsql - AND gec.categoryid IS NULL"; - - } else { - $sql = " FROM {glossary_entries} ge - JOIN {glossary_entries_categories} gec - ON gec.entryid = ge.id - AND gec.categoryid = :categoryid - LEFT JOIN {user} u ON ge.userid = u.id - WHERE $glossarysql - AND $approvedsql"; - $params['categoryid'] = $categoryid; + $qb->where('categoryid', 'entries_categories', null); } - $sqlselectcount = "SELECT COUNT('x')"; - $sqlselect = "SELECT $sqlfields"; - $sqlorder = ' ORDER BY ' . $sqlorderfields; + $qb->order_by('concept', 'entries'); + $qb->limit($from, $limit); // Fetching the entries. - $count = $DB->count_records_sql($sqlselectcount . $sql, $params); - $entries = $DB->get_records_sql($sqlselect . $sql . $sqlorder, $params, $from, $limit); + $count = $qb->count_records(); + $entries = $qb->get_records(); foreach ($entries as $key => $entry) { self::fill_entry_details($entry, $context); if ($entry->categoryid === null) { @@ -1088,49 +1040,31 @@ class mod_glossary_external extends external_api { } // Preparing the query. - $where = '1 = 1'; - $params = array(); - - if ($field == 'FIRSTNAME') { - $usernamefield = $DB->sql_fullname('u.firstname' , 'u.lastname'); - } else { - $usernamefield = $DB->sql_fullname('u.lastname' , 'u.firstname'); - } - - if ($letter != 'ALL' && $letter != 'SPECIAL' && ($letterstrlen = core_text::strlen($letter))) { - $params['hookup'] = core_text::strtoupper($letter); - $where = $DB->sql_substr("upper($usernamefield)", 1, $letterstrlen) . ' = :hookup'; + $firstnamefirst = $field === 'FIRSTNAME'; + $qb = new mod_glossary_entry_query_builder($glossary); + if ($letter != 'ALL' && $letter != 'SPECIAL' && core_text::strlen($letter)) { + $qb->filter_by_author_letter($letter, $firstnamefirst); } if ($letter == 'SPECIAL') { - $alphabet = explode(',', get_string('alphabet', 'langconfig')); - list($nia, $aparams) = $DB->get_in_or_equal($alphabet, SQL_PARAMS_NAMED, 'a', false); - $params = array_merge($params, $aparams); - $where = $DB->sql_substr("upper($usernamefield)", 1, 1) . " $nia"; + $qb->filter_by_author_non_letter($firstnamefirst); } - $approvedsql = '(ge.approved <> 0 OR ge.userid = :myid)'; - $params['myid'] = $USER->id; if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) { - $approvedsql = '1 = 1'; + $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL); + } else { + $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF); } - $userfields = user_picture::fields('u', null, 'userdataid', 'userdata'); - - $sqlselectcount = "SELECT COUNT('x')"; - $sqlselect = "SELECT ge.*, $userfields"; - $sql = " FROM {glossary_entries} ge - JOIN {user} u ON ge.userid = u.id - WHERE (ge.glossaryid = :gid1 OR ge.sourceglossaryid = :gid2) - AND $approvedsql - AND $where"; - $sqlorder = " ORDER BY $usernamefield $sort, ge.concept"; - - $params['gid1'] = $glossary->id; - $params['gid2'] = $glossary->id; + $qb->add_field('*', 'entries'); + $qb->join_user(true); + $qb->add_user_fields(); + $qb->order_by_author($firstnamefirst, $sort); + $qb->order_by('concept', 'entries'); + $qb->limit($from, $limit); // Fetching the entries. - $count = $DB->count_records_sql($sqlselectcount . $sql, $params); - $entries = $DB->get_records_sql($sqlselect . $sql . $sqlorder, $params, $from, $limit); + $count = $qb->count_records(); + $entries = $qb->get_records(); foreach ($entries as $key => $entry) { self::fill_entry_details($entry, $context); } @@ -1232,42 +1166,31 @@ class mod_glossary_external extends external_api { } // Preparing the query. - $params = array(); - - $approvedsql = '(ge.approved <> 0 OR ge.userid = :myid)'; - $params['myid'] = $USER->id; + $qb = new mod_glossary_entry_query_builder($glossary); if (!empty($options['includenotapproved']) && has_capability('mod/glossary:approve', $context)) { - $approvedsql = '1 = 1'; - } - - $userfields = user_picture::fields('u', null, 'userdataid', 'userdata'); - - $sqlselectcount = "SELECT COUNT('x')"; - $sqlselect = "SELECT ge.*, $userfields"; - $sql = " FROM {glossary_entries} ge - JOIN {user} u ON ge.userid = u.id - WHERE (ge.glossaryid = :gid1 OR ge.sourceglossaryid = :gid2) - AND $approvedsql - AND ge.userid = :uid"; - $params['uid'] = $authorid; - $params['gid1'] = $glossary->id; - $params['gid2'] = $glossary->id; - - $sqlorder = ' ORDER BY '; - if ($order == 'CREATION') { - $sqlorder .= 'ge.timecreated'; - - } else if ($order == 'UPDATE') { - $sqlorder .= 'ge.timemodified'; - + $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_ALL); } else { - $sqlorder .= 'ge.concept'; + $qb->filter_by_non_approved(mod_glossary_entry_query_builder::NON_APPROVED_SELF); } - $sqlorder .= ' ' . $sort; + + $qb->add_field('*', 'entries'); + $qb->join_user(true); + $qb->add_user_fields(); + $qb->where('id', 'user', $authorid); + + if ($order == 'CREATION') { + $qb->order_by('timecreated', 'entries', $sort); + } else if ($order == 'UPDATE') { + $qb->order_by('timemodified', 'entries', $sort); + } else { + $qb->order_by('concept', 'entries', $sort); + } + + $qb->limit($from, $limit); // Fetching the entries. - $count = $DB->count_records_sql($sqlselectcount . $sql, $params); - $entries = $DB->get_records_sql($sqlselect . $sql . $sqlorder, $params, $from, $limit); + $count = $qb->count_records(); + $entries = $qb->get_records(); foreach ($entries as $key => $entry) { self::fill_entry_details($entry, $context); }