See Release Notes
Long Term Support Release
<?php // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. declare(strict_types=1); namespace core_reportbuilder\local\helpers; use coding_exception; use core_text; /** * Helper functions for DB manipulations * * @package core_reportbuilder * @copyright 2019 Marina Glancy * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class database { /** @var string Prefix for generated aliases */ private const GENERATE_ALIAS_PREFIX = 'rbalias'; /** @var string Prefix for generated param names names */ private const GENERATE_PARAM_PREFIX = 'rbparam'; /** * Generates unique table/column alias that must be used in generated SQL * * @return string */ public static function generate_alias(): string { static $aliascount = 0; return static::GENERATE_ALIAS_PREFIX . ($aliascount++); }> /** > /** * Generates unique parameter name that must be used in generated SQL > * Generate multiple unique table/column aliases, see {@see generate_alias} for info * > * * @return string > * @param int $count */ > * @return string[] public static function generate_param_name(): string { > */ static $paramcount = 0; > public static function generate_aliases(int $count): array { > return array_map([ return static::GENERATE_PARAM_PREFIX . ($paramcount++); > static::class, 'generate_alias' } > ], array_fill(0, $count, null)); > } /** >* Validate that parameter names were generated using {@see generate_param_name}.> * Generate multiple unique parameter names, see {@see generate_param_name} for info * > * * @param array $params > * @param int $count * @return bool > * @return string[] * @throws coding_exception For invalid params. > */ */ > public static function generate_param_names(int $count): array { public static function validate_params(array $params): bool { > return array_map([ $nonmatchingkeys = array_filter($params, static function($key): bool { > static::class, 'generate_param_name' return !preg_match('/^' . static::GENERATE_PARAM_PREFIX . '[\d]+/', $key); > ], array_fill(0, $count, null)); }, ARRAY_FILTER_USE_KEY); > } > if (!empty($nonmatchingkeys)) { > /**throw new coding_exception('Invalid parameter names', implode(', ', array_keys($nonmatchingkeys))); } return true; } /** * Replace parameter names within given SQL expression, allowing caller to specify callback to handle their replacement * primarily to ensure uniqueness when the expression is to be used as part of a larger query * * @param string $sql * @param array $params * @param callable $callback Method that takes a single string parameter, and returns another string * @return string */ public static function sql_replace_parameter_names(string $sql, array $params, callable $callback): string { foreach ($params as $param) { // Pattern to look for param within the SQL. $pattern = '/:(?<param>' . preg_quote($param) . ')\b/'; $sql = preg_replace_callback($pattern, function(array $matches) use ($callback): string { return ':' . $callback($matches['param']); }, $sql); } return $sql; } /** * Generate SQL expression for sorting group concatenated fields * * @param string $field The original field or SQL expression * @param string|null $sort A valid SQL ORDER BY to sort the concatenated fields, if omitted then $field will be used * @return string */ public static function sql_group_concat_sort(string $field, string $sort = null): string { global $DB; // Fallback to sorting by the specified field, unless it contains parameters which would be duplicated. if ($sort === null && !preg_match('/[:?$]/', $field)) { $fieldsort = $field; } else { $fieldsort = $sort; } // Nothing to sort by. if ($fieldsort === null) { return ''; } // If the sort specifies a direction, we need to handle that differently in Postgres. if ($DB->get_dbfamily() === 'postgres') { $fieldsortdirection = ''; preg_match('/(?<direction>ASC|DESC)?$/i', $fieldsort, $matches); if (array_key_exists('direction', $matches)) { $fieldsortdirection = $matches['direction']; $fieldsort = core_text::substr($fieldsort, 0, -(core_text::strlen($fieldsortdirection))); } // Cast sort, stick the direction on the end.< $fieldsort = "CAST({$fieldsort} AS VARCHAR) {$fieldsortdirection}";> $fieldsort = $DB->sql_cast_to_char($fieldsort) . ' ' . $fieldsortdirection;} return $fieldsort; } }