Differences Between: [Versions 400 and 402] [Versions 401 and 402]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 declare(strict_types=1); 18 19 namespace core_reportbuilder\local\aggregation; 20 21 use lang_string; 22 use core_reportbuilder\local\helpers\database; 23 use core_reportbuilder\local\report\column; 24 25 /** 26 * Column group concatenation aggregation type 27 * 28 * @package core_reportbuilder 29 * @copyright 2021 Paul Holden <paulh@moodle.com> 30 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 31 */ 32 class groupconcat extends base { 33 34 /** @var string Character to use as a delimeter between column fields */ 35 protected const COLUMN_FIELD_DELIMETER = '<|>'; 36 37 /** @var string Character to use a null coalesce value */ 38 protected const COLUMN_NULL_COALESCE = '<^>'; 39 40 /** @var string Character to use as a delimeter between field values */ 41 protected const FIELD_VALUE_DELIMETER = '<,>'; 42 43 /** 44 * Return aggregation name 45 * 46 * @return lang_string 47 */ 48 public static function get_name(): lang_string { 49 return new lang_string('aggregationgroupconcat', 'core_reportbuilder'); 50 } 51 52 /** 53 * This aggregation can be performed on all non-timestamp columns 54 * 55 * @param int $columntype 56 * @return bool 57 */ 58 public static function compatible(int $columntype): bool { 59 return !in_array($columntype, [ 60 column::TYPE_TIMESTAMP, 61 ]); 62 } 63 64 /** 65 * Override base method to ensure all SQL fields are concatenated together if there are multiple 66 * 67 * @param array $sqlfields 68 * @return string 69 */ 70 public static function get_column_field_sql(array $sqlfields): string { 71 if (count($sqlfields) === 1) { 72 return parent::get_column_field_sql($sqlfields); 73 } 74 75 return self::get_column_fields_concat($sqlfields, self::COLUMN_FIELD_DELIMETER, self::COLUMN_NULL_COALESCE); 76 } 77 78 /** 79 * Return the aggregated field SQL 80 * 81 * @param string $field 82 * @param int $columntype 83 * @return string 84 */ 85 public static function get_field_sql(string $field, int $columntype): string { 86 global $DB; 87 88 $fieldsort = database::sql_group_concat_sort($field); 89 90 return $DB->sql_group_concat($field, self::FIELD_VALUE_DELIMETER, $fieldsort); 91 } 92 93 /** 94 * Return formatted value for column when applying aggregation, note we need to split apart the concatenated string 95 * and apply callbacks to each concatenated value separately 96 * 97 * @param mixed $value 98 * @param array $values 99 * @param array $callbacks 100 * @param int $columntype 101 * @return mixed 102 */ 103 public static function format_value($value, array $values, array $callbacks, int $columntype) { 104 $firstvalue = reset($values); 105 if ($firstvalue === null) { 106 return ''; 107 } 108 109 $formattedvalues = []; 110 111 // Store original names of all values that would be present without aggregation. 112 $valuenames = array_keys($values); 113 $valuenamescount = count($valuenames); 114 115 // Loop over each extracted value from the concatenated string. 116 $values = explode(self::FIELD_VALUE_DELIMETER, (string)$firstvalue); 117 foreach ($values as $value) { 118 119 // Ensure we have equal number of value names/data, account for truncation by DB. 120 $valuedata = explode(self::COLUMN_FIELD_DELIMETER, $value); 121 if ($valuenamescount !== count($valuedata)) { 122 continue; 123 } 124 125 // Re-construct original values, also ensuring any nulls contained within are restored. 126 $originalvalues = array_map(static function(string $value): ?string { 127 return $value === self::COLUMN_NULL_COALESCE ? null : $value; 128 }, array_combine($valuenames, $valuedata)); 129 130 $originalvalue = column::get_default_value($originalvalues, $columntype); 131 132 // Once we've re-constructed each value, we can apply callbacks to it. 133 $formattedvalues[] = parent::format_value($originalvalue, $originalvalues, $callbacks, $columntype); 134 } 135 136 $listseparator = get_string('listsep', 'langconfig') . ' '; 137 return implode($listseparator, $formattedvalues); 138 } 139 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body