MDL-82068 reportbuilder: re-factor SQL JOIN collection to trait.

Replace identical implementation of the same in various classes with
usage of the new trait. Implement constructor property promotion in
modified classes at the same time, to help clean up/clarify class
properties.
This commit is contained in:
Paul Holden 2024-05-23 19:04:33 +01:00
parent bcd8e0d6ed
commit 7c3b8c0a40
No known key found for this signature in database
GPG key ID: A81A96D6045F6164
12 changed files with 266 additions and 433 deletions

View file

@ -19,7 +19,7 @@ declare(strict_types=1);
namespace core_reportbuilder\local\entities; namespace core_reportbuilder\local\entities;
use coding_exception; use coding_exception;
use core_reportbuilder\local\helpers\database; use core_reportbuilder\local\helpers\{database, join_trait};
use core_reportbuilder\local\report\column; use core_reportbuilder\local\report\column;
use core_reportbuilder\local\report\filter; use core_reportbuilder\local\report\filter;
use lang_string; use lang_string;
@ -33,6 +33,8 @@ use lang_string;
*/ */
abstract class base { abstract class base {
use join_trait;
/** @var string $entityname Internal reference to name of entity */ /** @var string $entityname Internal reference to name of entity */
private $entityname = null; private $entityname = null;
@ -45,9 +47,6 @@ abstract class base {
/** @var array $tablejoinaliases Database tables that have already been joined to the report and their aliases */ /** @var array $tablejoinaliases Database tables that have already been joined to the report and their aliases */
private $tablejoinaliases = []; private $tablejoinaliases = [];
/** @var string[] $joins List of SQL joins for the entity */
private $joins = [];
/** @var column[] $columns List of columns for the entity */ /** @var column[] $columns List of columns for the entity */
private $columns = []; private $columns = [];
@ -253,39 +252,6 @@ abstract class base {
return array_key_exists($tablename, $this->tablejoinaliases); return array_key_exists($tablename, $this->tablejoinaliases);
} }
/**
* Add join clause required for this entity to join to existing tables/entities
*
* @param string $join
* @return self
*/
final public function add_join(string $join): self {
$this->joins[trim($join)] = trim($join);
return $this;
}
/**
* Add multiple join clauses required for this entity {@see add_join}
*
* @param string[] $joins
* @return self
*/
final public function add_joins(array $joins): self {
foreach ($joins as $join) {
$this->add_join($join);
}
return $this;
}
/**
* Return entity joins
*
* @return string[]
*/
final public function get_joins(): array {
return array_values($this->joins);
}
/** /**
* Helper method for returning joins necessary for retrieving tags related to the current entity * Helper method for returning joins necessary for retrieving tags related to the current entity
* *

View file

@ -40,66 +40,32 @@ use core_customfield\handler;
*/ */
class custom_fields { class custom_fields {
/** @var string $entityname Name of the entity */ use join_trait;
private $entityname;
/** @var handler $handler The handler for the customfields */ /** @var handler $handler The handler for the customfields */
private $handler; private handler $handler;
/** @var int $tablefieldalias The table alias and the field name (table.field) that matches the customfield instanceid. */
private $tablefieldalias;
/** @var array additional joins */
private $joins = [];
/** /**
* Class customfields constructor. * Constructor
* *
* @param string $tablefieldalias table alias and the field name (table.field) that matches the customfield instanceid. * @param string $tablefieldalias The table/field alias to match the instance ID when adding columns and filters.
* @param string $entityname name of the entity in the report where we add custom fields. * @param string $entityname The entity name used when adding columns and filters.
* @param string $component component name of full frankenstyle plugin name. * @param string $component component name of full frankenstyle plugin name.
* @param string $area name of the area (each component/plugin may define handlers for multiple areas). * @param string $area name of the area (each component/plugin may define handlers for multiple areas).
* @param int $itemid item id if the area uses them (usually not used). * @param int $itemid item id if the area uses them (usually not used).
*/ */
public function __construct(string $tablefieldalias, string $entityname, string $component, string $area, int $itemid = 0) { public function __construct(
$this->tablefieldalias = $tablefieldalias; /** @var string The table/field alias to match the instance ID when adding columns and filters */
$this->entityname = $entityname; private readonly string $tablefieldalias,
/** @var string The entity name used when adding columns and filters */
private readonly string $entityname,
string $component,
string $area,
int $itemid = 0,
) {
$this->handler = handler::get_handler($component, $area, $itemid); $this->handler = handler::get_handler($component, $area, $itemid);
} }
/**
* Additional join that is needed.
*
* @param string $join
* @return self
*/
public function add_join(string $join): self {
$this->joins[trim($join)] = trim($join);
return $this;
}
/**
* Additional joins that are needed.
*
* @param array $joins
* @return self
*/
public function add_joins(array $joins): self {
foreach ($joins as $join) {
$this->add_join($join);
}
return $this;
}
/**
* Return joins
*
* @return string[]
*/
private function get_joins(): array {
return array_values($this->joins);
}
/** /**
* Get table alias for given custom field * Get table alias for given custom field
* *

View file

@ -0,0 +1,65 @@
<?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;
/**
* Trait for classes that expect to store SQL table joins
*
* @package core_reportbuilder
* @copyright 2024 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
trait join_trait {
/** @var string[] SQL table joins */
private array $joins = [];
/**
* Add single SQL table join
*
* @param string $join
* @return static
*/
final public function add_join(string $join): static {
$this->joins[trim($join)] = trim($join);
return $this;
}
/**
* Add multiple SQL table joins
*
* @param string[] $joins
* @return static
*/
final public function add_joins(array $joins): static {
foreach ($joins as $join) {
$this->add_join($join);
}
return $this;
}
/**
* Return SQL table joins
*
* @return string[]
*/
final public function get_joins(): array {
return array_values($this->joins);
}
}

View file

@ -44,72 +44,28 @@ require_once($CFG->dirroot.'/user/profile/lib.php');
*/ */
class user_profile_fields { class user_profile_fields {
/** @var array user profile fields */ use join_trait;
private $userprofilefields;
/** @var string $entityname Name of the entity */ /** @var profile_field_base[] User profile fields */
private $entityname; private array $userprofilefields;
/** @var int $usertablefieldalias The user table/field alias */
private $usertablefieldalias;
/** @var array additional joins */
private $joins = [];
/** /**
* Class userprofilefields constructor. * Constructor
* *
* @param string $usertablefieldalias The user table/field alias used when adding columns and filters. * @param string $usertablefieldalias The table/field alias to match the user ID when adding columns and filters.
* @param string $entityname The entity name used when adding columns and filters. * @param string $entityname The entity name used when adding columns and filters.
*/ */
public function __construct(string $usertablefieldalias, string $entityname) { public function __construct(
$this->usertablefieldalias = $usertablefieldalias; /** @var string The table/field alias to match the user ID when adding columns and filters */
$this->entityname = $entityname; private readonly string $usertablefieldalias,
$this->userprofilefields = $this->get_user_profile_fields(); /** @var string The entity name used when adding columns and filters */
} private readonly string $entityname,
) {
/** // Retrieve the list of available/visible user profile fields.
* Retrieves the list of available/visible user profile fields $this->userprofilefields = array_filter(
* profile_get_user_fields_with_data(0),
* @return profile_field_base[] fn(profile_field_base $field) => $field->is_visible(),
*/ );
private function get_user_profile_fields(): array {
return array_filter(profile_get_user_fields_with_data(0), static function(profile_field_base $profilefield): bool {
return $profilefield->is_visible();
});
}
/**
* Additional join that is needed.
*
* @param string $join
* @return self
*/
public function add_join(string $join): self {
$this->joins[trim($join)] = trim($join);
return $this;
}
/**
* Additional joins that are needed.
*
* @param array $joins
* @return self
*/
public function add_joins(array $joins): self {
foreach ($joins as $join) {
$this->add_join($join);
}
return $this;
}
/**
* Return joins
*
* @return string[]
*/
private function get_joins(): array {
return array_values($this->joins);
} }
/** /**

View file

@ -20,8 +20,7 @@ namespace core_reportbuilder\local\report;
use coding_exception; use coding_exception;
use lang_string; use lang_string;
use core_reportbuilder\local\helpers\aggregation; use core_reportbuilder\local\helpers\{aggregation, database, join_trait};
use core_reportbuilder\local\helpers\database;
use core_reportbuilder\local\aggregation\base; use core_reportbuilder\local\aggregation\base;
use core_reportbuilder\local\models\column as column_model; use core_reportbuilder\local\models\column as column_model;
@ -34,6 +33,8 @@ use core_reportbuilder\local\models\column as column_model;
*/ */
final class column { final class column {
use join_trait;
/** @var int Column type is integer */ /** @var int Column type is integer */
public const TYPE_INTEGER = 1; public const TYPE_INTEGER = 1;
@ -55,24 +56,12 @@ final class column {
/** @var int $index Column index within a report */ /** @var int $index Column index within a report */
private $index; private $index;
/** @var string $columnname Internal reference to name of column */
private $columnname;
/** @var lang_string $columntitle Used as a title for the column in reports */
private $columntitle;
/** @var bool $hascustomcolumntitle Used to store if the column has been given a custom title */ /** @var bool $hascustomcolumntitle Used to store if the column has been given a custom title */
private $hascustomcolumntitle = false; private $hascustomcolumntitle = false;
/** @var string $entityname Name of the entity this column belongs to */
private $entityname;
/** @var int $type Column data type (one of the TYPE_* class constants) */ /** @var int $type Column data type (one of the TYPE_* class constants) */
private $type = self::TYPE_TEXT; private $type = self::TYPE_TEXT;
/** @var string[] $joins List of SQL joins for this column */
private $joins = [];
/** @var array $fields */ /** @var array $fields */
private $fields = []; private $fields = [];
@ -101,16 +90,16 @@ final class column {
private $attributes = []; private $attributes = [];
/** @var bool $available Used to know if column is available to the current user or not */ /** @var bool $available Used to know if column is available to the current user or not */
protected $available = true; private $available = true;
/** @var bool $deprecated */ /** @var bool $deprecated */
protected $deprecated = false; private $deprecated = false;
/** @var string $deprecatedmessage */ /** @var string $deprecatedmessage */
protected $deprecatedmessage; private $deprecatedmessage;
/** @var column_model $persistent */ /** @var column_model $persistent */
protected $persistent; private $persistent;
/** /**
* Column constructor * Column constructor
@ -129,10 +118,15 @@ final class column {
* this value should be the result of calling {@see get_entity_name}, however if creating columns inside reports directly * this value should be the result of calling {@see get_entity_name}, however if creating columns inside reports directly
* it should be the name of the entity as passed to {@see \core_reportbuilder\local\report\base::annotate_entity} * it should be the name of the entity as passed to {@see \core_reportbuilder\local\report\base::annotate_entity}
*/ */
public function __construct(string $name, ?lang_string $title, string $entityname) { public function __construct(
$this->columnname = $name; /** @var string Internal name of the column */
$this->columntitle = $title; private string $name,
$this->entityname = $entityname; /** @var lang_string|null Title of the column used in reports */
private ?lang_string $title,
/** @var string Name of the entity this column belongs to */
private readonly string $entityname,
) {
} }
/** /**
@ -142,7 +136,7 @@ final class column {
* @return self * @return self
*/ */
public function set_name(string $name): self { public function set_name(string $name): self {
$this->columnname = $name; $this->name = $name;
return $this; return $this;
} }
@ -152,7 +146,7 @@ final class column {
* @return mixed * @return mixed
*/ */
public function get_name(): string { public function get_name(): string {
return $this->columnname; return $this->name;
} }
/** /**
@ -162,7 +156,7 @@ final class column {
* @return self * @return self
*/ */
public function set_title(?lang_string $title): self { public function set_title(?lang_string $title): self {
$this->columntitle = $title; $this->title = $title;
$this->hascustomcolumntitle = true; $this->hascustomcolumntitle = true;
return $this; return $this;
} }
@ -173,7 +167,7 @@ final class column {
* @return string * @return string
*/ */
public function get_title(): string { public function get_title(): string {
return $this->columntitle ? (string) $this->columntitle : ''; return $this->title ? (string) $this->title : '';
} }
/** /**
@ -251,44 +245,6 @@ final class column {
return $this->type; return $this->type;
} }
/**
* Add join clause required for this column to join to existing tables/entities
*
* This is necessary in the case where {@see add_field} is selecting data from a table that isn't otherwise queried
*
* @param string $join
* @return self
*/
public function add_join(string $join): self {
$this->joins[trim($join)] = trim($join);
return $this;
}
/**
* Add multiple join clauses required for this column, passing each to {@see add_join}
*
* Typically when defining columns in entities, you should pass {@see \core_reportbuilder\local\report\base::get_joins} to
* this method, so that all entity joins are included in the report when your column is added to it
*
* @param string[] $joins
* @return self
*/
public function add_joins(array $joins): self {
foreach ($joins as $join) {
$this->add_join($join);
}
return $this;
}
/**
* Return column joins
*
* @return string[]
*/
public function get_joins(): array {
return array_values($this->joins);
}
/** /**
* Adds a field to be queried from the database that is necessary for this column * Adds a field to be queried from the database that is necessary for this column
* *

View file

@ -21,7 +21,7 @@ namespace core_reportbuilder\local\report;
use lang_string; use lang_string;
use moodle_exception; use moodle_exception;
use core_reportbuilder\local\filters\base; use core_reportbuilder\local\filters\base;
use core_reportbuilder\local\helpers\database; use core_reportbuilder\local\helpers\{database, join_trait};
use core_reportbuilder\local\models\filter as filter_model; use core_reportbuilder\local\models\filter as filter_model;
/** /**
@ -33,17 +33,7 @@ use core_reportbuilder\local\models\filter as filter_model;
*/ */
final class filter { final class filter {
/** @var string $filterclass */ use join_trait;
private $filterclass;
/** @var string $name */
private $name;
/** @var lang_string $header */
private $header;
/** @var string $entity */
private $entityname;
/** @var string $fieldsql */ /** @var string $fieldsql */
private $fieldsql = ''; private $fieldsql = '';
@ -51,26 +41,23 @@ final class filter {
/** @var array $fieldparams */ /** @var array $fieldparams */
private $fieldparams = []; private $fieldparams = [];
/** @var string[] $joins */
protected $joins = [];
/** @var bool $available */ /** @var bool $available */
protected $available = true; private $available = true;
/** @var bool $deprecated */ /** @var bool $deprecated */
protected $deprecated = false; private $deprecated = false;
/** @var string $deprecatedmessage */ /** @var string $deprecatedmessage */
protected $deprecatedmessage; private $deprecatedmessage;
/** @var mixed $options */ /** @var mixed $options */
protected $options; private $options;
/** @var array $limitoperators */ /** @var array $limitoperators */
protected $limitoperators = []; private $limitoperators = [];
/** @var filter_model $persistent */ /** @var filter_model $persistent */
protected $persistent; private $persistent;
/** /**
* Filter constructor * Filter constructor
@ -86,22 +73,21 @@ final class filter {
* @throws moodle_exception For invalid filter class * @throws moodle_exception For invalid filter class
*/ */
public function __construct( public function __construct(
string $filterclass, /** @var string Filter type class to use, must extend {@see base} filter class */
string $name, private readonly string $filterclass,
lang_string $header, /** @var string Internal name of the filter */
string $entityname, private readonly string $name,
/** @var lang_string Title of the filter used in reports */
private lang_string $header,
/** @var string Name of the entity this filter belongs to */
private readonly string $entityname,
string $fieldsql = '', string $fieldsql = '',
array $fieldparams = [] array $fieldparams = [],
) { ) {
if (!class_exists($filterclass) || !is_subclass_of($filterclass, base::class)) { if (!class_exists($filterclass) || !is_subclass_of($filterclass, base::class)) {
throw new moodle_exception('filterinvalid', 'reportbuilder', '', null, $filterclass); throw new moodle_exception('filterinvalid', 'reportbuilder', '', null, $filterclass);
} }
$this->filterclass = $filterclass;
$this->name = $name;
$this->header = $header;
$this->entityname = $entityname;
if ($fieldsql !== '') { if ($fieldsql !== '') {
$this->set_field_sql($fieldsql, $fieldparams); $this->set_field_sql($fieldsql, $fieldparams);
} }
@ -163,44 +149,6 @@ final class filter {
return $this->get_entity_name() . ':' . $this->get_name(); return $this->get_entity_name() . ':' . $this->get_name();
} }
/**
* Return joins
*
* @return string[]
*/
public function get_joins(): array {
return array_values($this->joins);
}
/**
* Add join clause required for this filter to join to existing tables/entities
*
* This is necessary in the case where {@see set_field_sql} is selecting data from a table that isn't otherwise queried
*
* @param string $join
* @return self
*/
public function add_join(string $join): self {
$this->joins[trim($join)] = trim($join);
return $this;
}
/**
* Add multiple join clauses required for this filter, passing each to {@see add_join}
*
* Typically when defining filters in entities, you should pass {@see \core_reportbuilder\local\report\base::get_joins} to
* this method, so that all entity joins are included in the report when your filter is used in it
*
* @param string[] $joins
* @return self
*/
public function add_joins(array $joins): self {
foreach ($joins as $join) {
$this->add_join($join);
}
return $this;
}
/** /**
* Get SQL expression for the field * Get SQL expression for the field
* *

View file

@ -43,7 +43,7 @@ defined('MOODLE_INTERNAL') || die();
* @copyright 2021 David Matamoros <davidmc@moodle.com> * @copyright 2021 David Matamoros <davidmc@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
class base_test extends advanced_testcase { final class base_test extends advanced_testcase {
/** /**
* Test entity table alias * Test entity table alias
@ -192,50 +192,6 @@ class base_test extends advanced_testcase {
$this->assertEquals($newtitle, $entity->get_entity_title()); $this->assertEquals($newtitle, $entity->get_entity_title());
} }
/**
* Test adding single join
*/
public function test_add_join(): void {
$entity = new base_test_entity();
$tablejoin = "JOIN {course} c2 ON c2.id = c1.id";
$entity->add_join($tablejoin);
$this->assertEquals([$tablejoin], $entity->get_joins());
}
/**
* Test adding multiple joins
*/
public function test_add_joins(): void {
$entity = new base_test_entity();
$tablejoins = [
"JOIN {course} c2 ON c2.id = c1.id",
"JOIN {course} c3 ON c3.id = c1.id",
];
$entity->add_joins($tablejoins);
$this->assertEquals($tablejoins, $entity->get_joins());
}
/**
* Test adding duplicate joins
*/
public function test_add_duplicate_joins(): void {
$entity = new base_test_entity();
$tablejoins = [
"JOIN {course} c2 ON c2.id = c1.id",
"JOIN {course} c3 ON c3.id = c1.id",
];
$entity
->add_joins($tablejoins)
->add_joins($tablejoins);
$this->assertEquals($tablejoins, $entity->get_joins());
}
/** /**
* Test getting column * Test getting column
*/ */

View file

@ -43,7 +43,7 @@ require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php");
* @copyright 2021 David Matamoros <davidmc@moodle.com> * @copyright 2021 David Matamoros <davidmc@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
class custom_fields_test extends core_reportbuilder_testcase { final class custom_fields_test extends core_reportbuilder_testcase {
/** /**
* Generate custom fields, one of each type * Generate custom fields, one of each type
@ -117,49 +117,34 @@ class custom_fields_test extends core_reportbuilder_testcase {
} }
/** /**
* Test for add_join * Test that joins added to the custom fields helper are present in its columns/filters
*/ */
public function test_add_join(): void { public function test_add_join(): void {
$this->resetAfterTest(); $this->resetAfterTest();
$customfields = $this->generate_customfields(); $customfields = $this->generate_customfields();
// By default, we always join on the customfield data table. // We always join on the customfield data table.
$columns = $customfields->get_columns(); $columnjoins = $customfields->get_columns()[0]->get_joins();
$joins = $columns[0]->get_joins(); $this->assertCount(1, $columnjoins);
$this->assertStringStartsWith('LEFT JOIN {customfield_data}', $columnjoins[0]);
$this->assertCount(1, $joins); $filterjoins = $customfields->get_filters()[0]->get_joins();
$this->assertStringStartsWith('LEFT JOIN {customfield_data}', $joins[0]); $this->assertCount(1, $filterjoins);
$this->assertStringStartsWith('LEFT JOIN {customfield_data}', $filterjoins[0]);
// Add additional join. // Add additional join.
$customfields->add_join('JOIN {test} t ON t.id = id'); $customfields->add_join('JOIN {test} t ON t.id = id');
$columns = $customfields->get_columns(); $columnjoins = $customfields->get_columns()[0]->get_joins();
$joins = $columns[0]->get_joins(); $this->assertCount(2, $columnjoins);
$this->assertEquals('JOIN {test} t ON t.id = id', $columnjoins[0]);
$this->assertStringStartsWith('LEFT JOIN {customfield_data}', $columnjoins[1]);
$this->assertCount(2, $joins); $filterjoins = $customfields->get_filters()[0]->get_joins();
$this->assertEquals('JOIN {test} t ON t.id = id', $joins[0]); $this->assertCount(2, $filterjoins);
$this->assertStringStartsWith('LEFT JOIN {customfield_data}', $joins[1]); $this->assertEquals('JOIN {test} t ON t.id = id', $filterjoins[0]);
} $this->assertStringStartsWith('LEFT JOIN {customfield_data}', $filterjoins[1]);
/**
* Test for add_joins
*/
public function test_add_joins(): void {
$this->resetAfterTest();
$customfields = $this->generate_customfields();
// Add additional joins.
$customfields->add_joins(['JOIN {test} t ON t.id = id', 'JOIN {test2} t2 ON t2.id = id']);
$columns = $customfields->get_columns();
$joins = $columns[0]->get_joins();
$this->assertCount(3, $joins);
$this->assertEquals('JOIN {test} t ON t.id = id', $joins[0]);
$this->assertEquals('JOIN {test2} t2 ON t2.id = id', $joins[1]);
$this->assertStringStartsWith('LEFT JOIN {customfield_data}', $joins[2]);
} }
/** /**
@ -254,7 +239,7 @@ class custom_fields_test extends core_reportbuilder_testcase {
* *
* @return array[] * @return array[]
*/ */
public function custom_report_filter_provider(): array { public static function custom_report_filter_provider(): array {
return [ return [
'Filter by text custom field' => ['course:customfield_text', [ 'Filter by text custom field' => ['course:customfield_text', [
'course:customfield_text_operator' => text::IS_EQUAL_TO, 'course:customfield_text_operator' => text::IS_EQUAL_TO,

View file

@ -0,0 +1,86 @@
<?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 advanced_testcase;
/**
* Unit tests for the join trait
*
* @package core_reportbuilder
* @covers \core_reportbuilder\local\helpers\join_trait
* @copyright 2024 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class join_trait_test extends advanced_testcase {
/**
* Test adding single join
*/
public function test_add_join(): void {
/** @var join_trait $trait */
$trait = $this->getObjectForTrait(join_trait::class);
$trait->add_join('JOIN {test} t ON t.id = a.id');
$this->assertEquals(['JOIN {test} t ON t.id = a.id'], $trait->get_joins());
}
/**
* Test adding single join multiple times
*/
public function test_add_join_multiple(): void {
/** @var join_trait $trait */
$trait = $this->getObjectForTrait(join_trait::class);
// Add multiple joins, two of which are duplicates.
$trait->add_join('JOIN {test} t1 ON t1.id = a.id')
->add_join('JOIN {test} t2 ON t2.id = b.id')
->add_join('JOIN {test} t1 ON t1.id = a.id');
// The duplicated join is normalised away.
$this->assertEquals([
'JOIN {test} t1 ON t1.id = a.id',
'JOIN {test} t2 ON t2.id = b.id',
], $trait->get_joins());
}
/**
* Test adding multiple joins
*/
public function test_add_joins(): void {
/** @var join_trait $trait */
$trait = $this->getObjectForTrait(join_trait::class);
// Add multiple joins, two of which are duplicates.
$trait->add_joins([
'JOIN {test} t1 ON t1.id = a.id',
'JOIN {test} t2 ON t2.id = b.id',
'JOIN {test} t1 ON t1.id = a.id',
]);
// The duplicated join is normalised away.
$this->assertEquals([
'JOIN {test} t1 ON t1.id = a.id',
'JOIN {test} t2 ON t2.id = b.id',
], $trait->get_joins());
}
}

View file

@ -42,7 +42,7 @@ require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php");
* @copyright 2021 David Matamoros <davidmc@moodle.com> * @copyright 2021 David Matamoros <davidmc@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
class user_profile_fields_test extends core_reportbuilder_testcase { final class user_profile_fields_test extends core_reportbuilder_testcase {
/** /**
* Generate custom profile fields, one of each type * Generate custom profile fields, one of each type
@ -132,33 +132,34 @@ class user_profile_fields_test extends core_reportbuilder_testcase {
} }
/** /**
* Test for add_join * Test that joins added to the profile fields helper are present in its columns/filters
*/ */
public function test_add_join(): void { public function test_add_join(): void {
$this->resetAfterTest(); $this->resetAfterTest();
$userprofilefields = $this->generate_userprofilefields(); $userprofilefields = $this->generate_userprofilefields();
$columns = $userprofilefields->get_columns();
$this->assertCount(1, ($columns[0])->get_joins());
// We always join on the user info data table.
$columnjoins = $userprofilefields->get_columns()[0]->get_joins();
$this->assertCount(1, $columnjoins);
$this->assertStringStartsWith('LEFT JOIN {user_info_data}', $columnjoins[0]);
$filterjoins = $userprofilefields->get_filters()[0]->get_joins();
$this->assertCount(1, $filterjoins);
$this->assertStringStartsWith('LEFT JOIN {user_info_data}', $filterjoins[0]);
// Add additional join.
$userprofilefields->add_join('JOIN {test} t ON t.id = id'); $userprofilefields->add_join('JOIN {test} t ON t.id = id');
$columns = $userprofilefields->get_columns();
$this->assertCount(2, ($columns[0])->get_joins());
}
/** $columnjoins = $userprofilefields->get_columns()[0]->get_joins();
* Test for add_joins $this->assertCount(2, $columnjoins);
*/ $this->assertEquals('JOIN {test} t ON t.id = id', $columnjoins[0]);
public function test_add_joins(): void { $this->assertStringStartsWith('LEFT JOIN {user_info_data}', $columnjoins[1]);
$this->resetAfterTest();
$userprofilefields = $this->generate_userprofilefields(); $filterjoins = $userprofilefields->get_filters()[0]->get_joins();
$columns = $userprofilefields->get_columns(); $this->assertCount(2, $filterjoins);
$this->assertCount(1, ($columns[0])->get_joins()); $this->assertEquals('JOIN {test} t ON t.id = id', $filterjoins[0]);
$this->assertStringStartsWith('LEFT JOIN {user_info_data}', $filterjoins[1]);
$userprofilefields->add_joins(['JOIN {test} t ON t.id = id', 'JOIN {test2} t2 ON t2.id = id']);
$columns = $userprofilefields->get_columns();
$this->assertCount(3, ($columns[0])->get_joins());
} }
/** /**
@ -262,7 +263,7 @@ class user_profile_fields_test extends core_reportbuilder_testcase {
* *
* @return array[] * @return array[]
*/ */
public function custom_report_filter_provider(): array { public static function custom_report_filter_provider(): array {
return [ return [
'Filter by checkbox profile field' => ['user:profilefield_checkbox', [ 'Filter by checkbox profile field' => ['user:profilefield_checkbox', [
'user:profilefield_checkbox_operator' => boolean_select::CHECKED, 'user:profilefield_checkbox_operator' => boolean_select::CHECKED,

View file

@ -32,7 +32,7 @@ use core_reportbuilder\local\helpers\database;
* @copyright 2020 Paul Holden <paulh@moodle.com> * @copyright 2020 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
class column_test extends advanced_testcase { final class column_test extends advanced_testcase {
/** /**
* Test column name getter/setter * Test column name getter/setter
@ -112,38 +112,12 @@ class column_test extends advanced_testcase {
$column->set_type(-1); $column->set_type(-1);
} }
/**
* Test adding single join
*/
public function test_add_join(): void {
$column = $this->create_column('test');
$this->assertEquals([], $column->get_joins());
$column->add_join('JOIN {user} u ON u.id = table.userid');
$this->assertEquals(['JOIN {user} u ON u.id = table.userid'], $column->get_joins());
}
/**
* Test adding multiple joins
*/
public function test_add_joins(): void {
$tablejoins = [
"JOIN {course} c2 ON c2.id = c1.id",
"JOIN {course} c3 ON c3.id = c1.id",
];
$column = $this->create_column('test')
->add_joins($tablejoins);
$this->assertEquals($tablejoins, $column->get_joins());
}
/** /**
* Data provider for {@see test_add_field} * Data provider for {@see test_add_field}
* *
* @return array * @return array
*/ */
public function add_field_provider(): array { public static function add_field_provider(): array {
return [ return [
['foo', '', ['foo AS c1_foo']], ['foo', '', ['foo AS c1_foo']],
['foo', 'bar', ['foo AS c1_bar']], ['foo', 'bar', ['foo AS c1_bar']],
@ -231,7 +205,7 @@ class column_test extends advanced_testcase {
* *
* @return array * @return array
*/ */
public function add_fields_provider(): array { public static function add_fields_provider(): array {
return [ return [
['t.foo', ['t.foo AS c1_foo']], ['t.foo', ['t.foo AS c1_foo']],
['t.foo bar', ['t.foo AS c1_bar']], ['t.foo bar', ['t.foo AS c1_bar']],
@ -327,7 +301,7 @@ class column_test extends advanced_testcase {
* *
* @return array[] * @return array[]
*/ */
public function column_type_provider(): array { public static function column_type_provider(): array {
return [ return [
[column::TYPE_INTEGER, 42], [column::TYPE_INTEGER, 42],
[column::TYPE_TEXT, 'Hello'], [column::TYPE_TEXT, 'Hello'],

View file

@ -31,7 +31,7 @@ use core_reportbuilder\local\filters\text;
* @copyright 2021 Paul Holden <paulh@moodle.com> * @copyright 2021 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
class filter_test extends advanced_testcase { final class filter_test extends advanced_testcase {
/** /**
* Test getting filter class * Test getting filter class
@ -120,32 +120,6 @@ class filter_test extends advanced_testcase {
$this->assertEquals(['username_1' => 'test', 'idnumber_1' => 'bar'], $params); $this->assertEquals(['username_1' => 'test', 'idnumber_1' => 'bar'], $params);
} }
/**
* Test adding single join
*/
public function test_add_join(): void {
$filter = $this->create_filter('username', 'u.username');
$this->assertEquals([], $filter->get_joins());
$filter->add_join('JOIN {user} u ON u.id = table.userid');
$this->assertEquals(['JOIN {user} u ON u.id = table.userid'], $filter->get_joins());
}
/**
* Test adding multiple joins
*/
public function test_add_joins(): void {
$tablejoins = [
"JOIN {course} c2 ON c2.id = c1.id",
"JOIN {course} c3 ON c3.id = c1.id",
];
$filter = $this->create_filter('username', 'u.username')
->add_joins($tablejoins);
$this->assertEquals($tablejoins, $filter->get_joins());
}
/** /**
* Test is available * Test is available
*/ */