diff --git a/Sources/Alert.php b/Sources/Alert.php index 90d7b49d65..7109e25b47 100644 --- a/Sources/Alert.php +++ b/Sources/Alert.php @@ -774,6 +774,7 @@ public static function load(int|array $ids = [], array $query_customizations = [ $joins = $query_customizations['joins'] ?? []; $where = $query_customizations['where'] ?? []; $order = $query_customizations['order'] ?? ['a.id_alert DESC']; + $group = $query_customizations['group'] ?? []; $limit = $query_customizations['limit'] ?? min(!empty(Config::$modSettings['alerts_per_page']) ? Config::$modSettings['alerts_per_page'] : 1000, 1000); $params = $query_customizations['params'] ?? []; @@ -1594,12 +1595,14 @@ protected static function deleteInvisible(array &$alerts, int $memID): void * If this is left empty, no WHERE clause will be used. * @param array $order Zero or more conditions for the ORDER BY clause. * If this is left empty, no ORDER BY clause will be used. + * @param array $group Zero or more conditions for the GROUP BY clause. + * If this is left empty, no GROUP BY clause will be used. * @param int|string $limit Maximum number of results to retrieve. * If this is left empty, all results will be retrieved. * * @return Generator Iterating over the result gives database rows. */ - protected static function queryData(array $selects, array $params = [], array $joins = [], array $where = [], array $order = [], int|string $limit = 0) + protected static function queryData(array $selects, array $params = [], array $joins = [], array $where = [], array $order = [], array $group = [], int|string $limit = 0) { $request = Db::$db->query( '', @@ -1607,7 +1610,8 @@ protected static function queryData(array $selects, array $params = [], array $j ' . implode(', ', $selects) . ' FROM {db_prefix}user_alerts AS a' . (empty($joins) ? '' : ' ' . implode("\n\t\t\t\t", $joins)) . (empty($where) ? '' : ' - WHERE (' . implode(') AND (', $where) . ')') . (empty($order) ? '' : ' + WHERE (' . implode(') AND (', $where) . ')') . (empty($group) ? '' : ' + GROUP BY ' . implode(', ', $group)) . (empty($order) ? '' : ' ORDER BY ' . implode(', ', $order)) . (!empty($limit) ? ' LIMIT ' . $limit : ''), $params, diff --git a/Sources/Board.php b/Sources/Board.php index 368652a822..de88872c60 100644 --- a/Sources/Board.php +++ b/Sources/Board.php @@ -53,7 +53,6 @@ class Board implements \ArrayAccess 'getModeratorGroups' => 'getBoardModeratorGroups', 'isChildOf' => 'isChildOf', 'getParents' => 'getBoardParents', - 'queryData' => 'queryData', ], 'prop_names' => [ 'board_id' => 'board', @@ -2189,12 +2188,14 @@ public static function getParents(int $id_parent): array * If this is left empty, no WHERE clause will be used. * @param array $order Zero or more conditions for the ORDER BY clause. * If this is left empty, no ORDER BY clause will be used. + * @param array $group Zero or more conditions for the GROUP BY clause. + * If this is left empty, no GROUP BY clause will be used. * @param int $limit Maximum number of results to retrieve. * If this is left empty, all results will be retrieved. * * @return Generator Iterating over the result gives database rows. */ - public static function queryData(array $selects, array $params = [], array $joins = [], array $where = [], array $order = [], int $limit = 0) + public static function queryData(array $selects, array $params = [], array $joins = [], array $where = [], array $order = [], array $group = [], int $limit = 0) { // If we only want some child boards, use a CTE query for improved performance. if (!empty($params['id_parent']) && in_array('b.id_parent != 0', $where) && Db::$db->cte_support()) { diff --git a/Sources/Category.php b/Sources/Category.php index 34b35f29c6..3715d076ea 100644 --- a/Sources/Category.php +++ b/Sources/Category.php @@ -262,11 +262,11 @@ public function unparseDescription(): void /** * Loads categories by ID number and/or by custom query. * - * If both arguments are empty, loads nothing. + * If both arguments are empty, loads all categories. * - * @param array|int $ids The ID numbers of zero or more boards. + * @param array|int $ids The ID numbers of zero or more categories. * @param array $query_customizations Customizations to the SQL query. - * @return array Instances of this class for the loaded boards. + * @return array Instances of this class for the loaded categories. */ public static function load(array|int $ids = [], array $query_customizations = []): array { @@ -274,14 +274,10 @@ public static function load(array|int $ids = [], array $query_customizations = [ $ids = array_unique(array_map('intval', (array) $ids)); - if (empty($query_customizations) && empty($ids)) { - return $loaded; - } - $selects = $query_customizations['selects'] ?? ['c.*']; $joins = $query_customizations['joins'] ?? []; $where = $query_customizations['where'] ?? []; - $order = $query_customizations['order'] ?? []; + $order = $query_customizations['order'] ?? ['c.cat_order']; $group = $query_customizations['group'] ?? []; $limit = $query_customizations['limit'] ?? 0; $params = $query_customizations['params'] ?? []; @@ -291,7 +287,7 @@ public static function load(array|int $ids = [], array $query_customizations = [ $params['ids'] = $ids; } - foreach (Board::queryData($selects, $params, $joins, $where, $order, $group, $limit) as $row) { + foreach (self::queryData($selects, $params, $joins, $where, $order, $group, $limit) as $row) { $row['id_cat'] = (int) $row['id_cat']; if (isset(self::$loaded[$row['id_cat']])) { @@ -674,7 +670,7 @@ public static function getTree(): void 'c.name AS cat_name', 'c.description AS cat_desc', ]; $params = []; - $joins = ['LEFT JOIN {db_prefix}categories AS c ON (b.id_cat = c.id_cat)']; + $joins = ['LEFT JOIN {db_prefix}boards AS b ON (b.id_cat = c.id_cat)']; $where = ['{query_see_board}']; $order = ['c.cat_order', 'b.child_level', 'b.board_order']; @@ -690,8 +686,10 @@ public static function getTree(): void // Getting all the board and category information you'd ever wanted. self::$loaded = []; $last_board_order = 0; + $prevBoard = 0; + $curLevel = 0; - foreach (Board::queryData($selects, $params, $joins, $where, $order) as $row) { + foreach (self::queryData($selects, $params, $joins, $where, $order) as $row) { if (!isset(self::$loaded[$row['id_cat']])) { self::init($row['id_cat'], [ 'name' => $row['cat_name'], @@ -845,6 +843,52 @@ function ($a, $b) { $this->is_first = true; } } + + /************************* + * Internal static methods + *************************/ + + /** + * Generator that runs queries about category data and yields the result rows. + * + * @param array $selects Table columns to select. + * @param array $params Parameters to substitute into query text. + * @param array $joins Zero or more *complete* JOIN clauses. + * E.g.: 'LEFT JOIN {db_prefix}boards AS b ON (c.id_cat = b.id_cat)' + * Note: 'FROM {db_prefix}categories AS c' is always part of the query. + * @param array $where Zero or more conditions for the WHERE clause. + * Conditions will be placed in parentheses and concatenated with AND. + * If this is left empty, no WHERE clause will be used. + * @param array $order Zero or more conditions for the ORDER BY clause. + * If this is left empty, no ORDER BY clause will be used. + * @param array $group Zero or more conditions for the GROUP BY clause. + * If this is left empty, no GROUP BY clause will be used. + * @param int|string $limit Maximum number of results to retrieve. + * If this is left empty, all results will be retrieved. + * + * @return Generator Iterating over the result gives database rows. + */ + protected static function queryData(array $selects, array $params = [], array $joins = [], array $where = [], array $order = [], array $group = [], int|string $limit = 0) + { + $request = Db::$db->query( + '', + 'SELECT + ' . implode(', ', $selects) . ' + FROM {db_prefix}categories AS c' . (empty($joins) ? '' : ' + ' . implode("\n\t\t\t\t", $joins)) . (empty($where) ? '' : ' + WHERE (' . implode(') AND (', $where) . ')') . (empty($group) ? '' : ' + GROUP BY ' . implode(', ', $group)) . (empty($order) ? '' : ' + ORDER BY ' . implode(', ', $order)) . (!empty($limit) ? ' + LIMIT ' . $limit : ''), + $params, + ); + + while ($row = Db::$db->fetch_assoc($request)) { + yield $row; + } + + Db::$db->free_result($request); + } } // Export public static functions and properties to global namespace for backward compatibility. diff --git a/Sources/Event.php b/Sources/Event.php index 15f05b1288..11312e9160 100644 --- a/Sources/Event.php +++ b/Sources/Event.php @@ -955,6 +955,7 @@ public static function get(string $low_date, string $high_date, bool $use_permis 'cal.end_date >= {date:low_date}', ]; $order = $query_customizations['order'] ?? []; + $group = $query_customizations['group'] ?? []; $limit = $query_customizations['limit'] ?? 0; $params = $query_customizations['params'] ?? [ 'high_date' => $high_date, @@ -966,9 +967,9 @@ public static function get(string $low_date, string $high_date, bool $use_permis $where[] = '(cal.id_board = {int:no_board_link} OR {query_wanna_see_board})'; } - IntegrationHook::call('integrate_query_event', [&$selects, &$params, &$joins, &$where, &$order, &$limit]); + IntegrationHook::call('integrate_query_event', [&$selects, &$params, &$joins, &$where, &$order, &$group, &$limit]); - foreach(self::queryData($selects, $params, $joins, $where, $order, $limit) as $row) { + foreach(self::queryData($selects, $params, $joins, $where, $order, $group, $limit) as $row) { // If the attached topic is not approved then for the moment pretend it doesn't exist. if (!empty($row['id_first_msg']) && Config::$modSettings['postmod_active'] && !$row['approved']) { continue; @@ -1169,12 +1170,14 @@ protected function fixEndDate(): void * If this is left empty, no WHERE clause will be used. * @param array $order Zero or more conditions for the ORDER BY clause. * If this is left empty, no ORDER BY clause will be used. + * @param array $group Zero or more conditions for the GROUP BY clause. + * If this is left empty, no GROUP BY clause will be used. * @param int|string $limit Maximum number of results to retrieve. * If this is left empty, all results will be retrieved. * * @return Generator Iterating over the result gives database rows. */ - protected static function queryData(array $selects, array $params = [], array $joins = [], array $where = [], array $order = [], int|string $limit = 0) + protected static function queryData(array $selects, array $params = [], array $joins = [], array $where = [], array $order = [], array $group = [], int|string $limit = 0) { $request = Db::$db->query( '', @@ -1182,7 +1185,8 @@ protected static function queryData(array $selects, array $params = [], array $j ' . implode(', ', $selects) . ' FROM {db_prefix}calendar AS cal' . (empty($joins) ? '' : ' ' . implode("\n\t\t\t\t", $joins)) . (empty($where) ? '' : ' - WHERE (' . implode(') AND (', $where) . ')') . (empty($order) ? '' : ' + WHERE (' . implode(') AND (', $where) . ')') . (empty($group) ? '' : ' + GROUP BY ' . implode(', ', $group)) . (empty($order) ? '' : ' ORDER BY ' . implode(', ', $order)) . (!empty($limit) ? ' LIMIT ' . $limit : ''), $params, diff --git a/Sources/Group.php b/Sources/Group.php index eb06eae78e..35b394e749 100644 --- a/Sources/Group.php +++ b/Sources/Group.php @@ -2611,6 +2611,8 @@ protected static function getLink(object $group): string * If this is left empty, no WHERE clause will be used. * @param array $order Zero or more conditions for the ORDER BY clause. * If this is left empty, no ORDER BY clause will be used. + * @param array $group Zero or more conditions for the GROUP BY clause. + * If this is left empty, no GROUP BY clause will be used. * @param int|string $limit Maximum number of results to retrieve. * If this is left empty, all results will be retrieved. * diff --git a/Sources/Msg.php b/Sources/Msg.php index 3b2c328fd3..4f7876b64a 100644 --- a/Sources/Msg.php +++ b/Sources/Msg.php @@ -575,6 +575,7 @@ public static function get($ids, array $query_customizations = []) $order = $query_customizations['order'] ?? [ 'm.id_msg' . (empty(Theme::$current->options['view_newest_first']) ? '' : ' DESC'), ]; + $group = $query_customizations['group'] ?? []; $limit = $query_customizations['limit'] ?? 0; $params = $query_customizations['params'] ?? [ 'new_from' => Topic::$info->new_from ?? Config::$modSettings['maxMsgID'] + 1, @@ -587,9 +588,9 @@ public static function get($ids, array $query_customizations = []) // Just FYI, for historical reasons the order in which the arguments are // passed to this hook is different than the order in which they are // passed to the queryData() method. - IntegrationHook::call('integrate_query_message', [&$selects, &$joins, &$params, &$where, &$order, &$limit]); + IntegrationHook::call('integrate_query_message', [&$selects, &$joins, &$params, &$where, &$order, &$group, &$limit]); - foreach(self::queryData($selects, $params, $joins, $where, $order, $limit) as $row) { + foreach(self::queryData($selects, $params, $joins, $where, $order, $group, $limit) as $row) { $id = (int) $row['id_msg']; yield (new self($id, $row)); @@ -3009,12 +3010,14 @@ public static function spell_suggest($dict, $word): array * If this is left empty, no WHERE clause will be used. * @param array $order Zero or more conditions for the ORDER BY clause. * If this is left empty, no ORDER BY clause will be used. + * @param array $group Zero or more conditions for the GROUP BY clause. + * If this is left empty, no GROUP BY clause will be used. * @param int|string $limit Maximum number of results to retrieve. * If this is left empty, all results will be retrieved. * * @return Generator Iterating over the result gives database rows. */ - protected static function queryData(array $selects, array $params = [], array $joins = [], array $where = [], array $order = [], int|string $limit = 0) + protected static function queryData(array $selects, array $params = [], array $joins = [], array $where = [], array $order = [], array $group = [], int|string $limit = 0) { self::$messages_request = Db::$db->query( '', @@ -3022,7 +3025,8 @@ protected static function queryData(array $selects, array $params = [], array $j ' . implode(', ', $selects) . ' FROM {db_prefix}messages AS m' . (empty($joins) ? '' : ' ' . implode("\n\t\t\t\t", $joins)) . (empty($where) ? '' : ' - WHERE (' . implode(') AND (', $where) . ')') . (empty($order) ? '' : ' + WHERE (' . implode(') AND (', $where) . ')') . (empty($group) ? '' : ' + GROUP BY ' . implode(', ', $group)) . (empty($order) ? '' : ' ORDER BY ' . implode(', ', $order)) . (!empty($limit) ? ' LIMIT ' . $limit : ''), $params, diff --git a/Sources/Search/SearchResult.php b/Sources/Search/SearchResult.php index 3569966ef5..775315fe00 100644 --- a/Sources/Search/SearchResult.php +++ b/Sources/Search/SearchResult.php @@ -462,6 +462,7 @@ public static function get($ids, array $query_customizations = []) Db::$db->custom_order('m.id_msg', $ids), ]; + $group = $query_customizations['group'] ?? []; $limit = $query_customizations['limit'] ?? '{int:limit}'; $params = $query_customizations['params'] ?? [ @@ -475,7 +476,7 @@ public static function get($ids, array $query_customizations = []) $params['message_list'] = self::$messages_to_get = array_filter(array_unique(array_map('intval', (array) $ids))); } - foreach(self::queryData($selects, $params, $joins, $where, $order, $limit) as $row) { + foreach(self::queryData($selects, $params, $joins, $where, $order, $group, $limit) as $row) { $id = (int) $row['id_msg']; yield (new self($id, $row));