MDL-75449 reportbuilder: add no tags/exclude tags filter operators.

Allow user to filter for elements that either contain no tags, plus
those that don't contain selected tags.
This commit is contained in:
Paul Holden 2022-08-18 13:02:56 +01:00
parent 3bc792b9b8
commit fd01461b7a
3 changed files with 29 additions and 2 deletions

View file

@ -45,6 +45,12 @@ class tags extends base {
/** @var int Filter for selected tags */ /** @var int Filter for selected tags */
public const EQUAL_TO = 2; public const EQUAL_TO = 2;
/** @var int Tags are not present */
public const EMPTY = 3;
/** @var int Filter for excluded tags */
public const NOT_EQUAL_TO = 4;
/** /**
* Returns an array of comparison operators * Returns an array of comparison operators
* *
@ -54,7 +60,9 @@ class tags extends base {
$operators = [ $operators = [
self::ANY_VALUE => new lang_string('filterisanyvalue', 'core_reportbuilder'), self::ANY_VALUE => new lang_string('filterisanyvalue', 'core_reportbuilder'),
self::NOT_EMPTY => new lang_string('filterisnotempty', 'core_reportbuilder'), self::NOT_EMPTY => new lang_string('filterisnotempty', 'core_reportbuilder'),
self::EMPTY => new lang_string('filterisempty', 'core_reportbuilder'),
self::EQUAL_TO => new lang_string('filterisequalto', 'core_reportbuilder'), self::EQUAL_TO => new lang_string('filterisequalto', 'core_reportbuilder'),
self::NOT_EQUAL_TO => new lang_string('filterisnotequalto', 'core_reportbuilder'),
]; ];
return $this->filter->restrict_limited_operators($operators); return $this->filter->restrict_limited_operators($operators);
@ -84,7 +92,7 @@ class tags extends base {
$valuelabel = get_string('filterfieldvalue', 'core_reportbuilder', $this->get_header()); $valuelabel = get_string('filterfieldvalue', 'core_reportbuilder', $this->get_header());
$mform->addElement('autocomplete', "{$this->name}_value", $valuelabel, $tags, ['multiple' => true]) $mform->addElement('autocomplete', "{$this->name}_value", $valuelabel, $tags, ['multiple' => true])
->setHiddenLabel(true); ->setHiddenLabel(true);
$mform->hideIf("{$this->name}_value", "{$this->name}_operator", 'neq', self::EQUAL_TO); $mform->hideIf("{$this->name}_value", "{$this->name}_operator", 'in', [self::ANY_VALUE, self::EMPTY, self::NOT_EMPTY]);
} }
/** /**
@ -104,12 +112,21 @@ class tags extends base {
if ($operator === self::NOT_EMPTY) { if ($operator === self::NOT_EMPTY) {
$select = "{$fieldsql} IS NOT NULL"; $select = "{$fieldsql} IS NOT NULL";
} else if ($operator === self::EMPTY) {
$select = "{$fieldsql} IS NULL";
} else if ($operator === self::EQUAL_TO && !empty($tags)) { } else if ($operator === self::EQUAL_TO && !empty($tags)) {
[$tagselect, $tagselectparams] = $DB->get_in_or_equal($tags, SQL_PARAMS_NAMED, [$tagselect, $tagselectparams] = $DB->get_in_or_equal($tags, SQL_PARAMS_NAMED,
database::generate_param_name() . '_'); database::generate_param_name() . '_');
$select = "{$fieldsql} {$tagselect}"; $select = "{$fieldsql} {$tagselect}";
$params = array_merge($params, $tagselectparams); $params = array_merge($params, $tagselectparams);
} else if ($operator === self::NOT_EQUAL_TO && !empty($tags)) {
[$tagselect, $tagselectparams] = $DB->get_in_or_equal($tags, SQL_PARAMS_NAMED,
database::generate_param_name() . '_', false);
// We should also return those elements that aren't tagged at all.
$select = "COALESCE({$fieldsql}, 0) {$tagselect}";
$params = array_merge($params, $tagselectparams);
} else { } else {
// Invalid/inactive (any value) filter.. // Invalid/inactive (any value) filter..
return ['', []]; return ['', []];

View file

@ -42,8 +42,11 @@ class tags_test extends advanced_testcase {
return [ return [
'Any value' => [tags::ANY_VALUE, null, ['course01', 'course01', 'course02', 'course03']], 'Any value' => [tags::ANY_VALUE, null, ['course01', 'course01', 'course02', 'course03']],
'Not empty' => [tags::NOT_EMPTY, null, ['course01', 'course01', 'course02']], 'Not empty' => [tags::NOT_EMPTY, null, ['course01', 'course01', 'course02']],
'Empty' => [tags::EMPTY, null, ['course03']],
'Equal to unselected' => [tags::EQUAL_TO, null, ['course01', 'course01', 'course02', 'course03']], 'Equal to unselected' => [tags::EQUAL_TO, null, ['course01', 'course01', 'course02', 'course03']],
'Equal to selected tag' => [tags::EQUAL_TO, 'cat', ['course01']], 'Equal to selected tag' => [tags::EQUAL_TO, 'cat', ['course01']],
'Not equal to unselected' => [tags::NOT_EQUAL_TO, null, ['course01', 'course01', 'course02', 'course03']],
'Not equal to selected tag' => [tags::NOT_EQUAL_TO, 'fish', ['course01', 'course01', 'course03']],
]; ];
} }

View file

@ -166,10 +166,17 @@ class tags_test extends core_reportbuilder_testcase {
], false], ], false],
// Tag. // Tag.
'Filter tag name' => ['tag:name', [ 'Filter tag name equal to' => ['tag:name', [
'tag:name_operator' => tags_filter::EQUAL_TO, 'tag:name_operator' => tags_filter::EQUAL_TO,
'tag:name_value' => [-1], 'tag:name_value' => [-1],
], false], ], false],
'Filter tag name not equal to' => ['tag:name', [
'tag:name_operator' => tags_filter::NOT_EQUAL_TO,
'tag:name_value' => [-1],
], true],
'Filter tag name empty' => ['tag:name', [
'tag:name_operator' => tags_filter::EMPTY,
], false],
'Filter tag name not empty' => ['tag:name', [ 'Filter tag name not empty' => ['tag:name', [
'tag:name_operator' => tags_filter::NOT_EMPTY, 'tag:name_operator' => tags_filter::NOT_EMPTY,
], true], ], true],