AutorÃa | Ultima modificación | Ver Log |
<?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\report;
use lang_string;
use moodle_exception;
use core_reportbuilder\local\filters\base;
use core_reportbuilder\local\helpers\database;
use core_reportbuilder\local\models\filter as filter_model;
/**
* Class to represent a report filter
*
* @package core_reportbuilder
* @copyright 2021 Paul Holden <paulh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class filter {
/** @var string $filterclass */
private $filterclass;
/** @var string $name */
private $name;
/** @var lang_string $header */
private $header;
/** @var string $entity */
private $entityname;
/** @var string $fieldsql */
private $fieldsql = '';
/** @var array $fieldparams */
private $fieldparams = [];
/** @var string[] $joins */
protected $joins = [];
/** @var bool $available */
protected $available = true;
/** @var bool $deprecated */
protected $deprecated = false;
/** @var string $deprecatedmessage */
protected $deprecatedmessage;
/** @var mixed $options */
protected $options;
/** @var array $limitoperators */
protected $limitoperators = [];
/** @var filter_model $persistent */
protected $persistent;
/**
* Filter constructor
*
* @param string $filterclass Filter type class to use, must extend {@see base} filter class
* @param string $name Internal name of the filter
* @param lang_string $header Title of the filter used in reports
* @param string $entityname Name of the entity this filter belongs to. Typically when creating filters within entities
* this value should be the result of calling {@see get_entity_name}, however if creating filters inside reports directly
* it should be the name of the entity as passed to {@see \core_reportbuilder\local\report\base::annotate_entity}
* @param string $fieldsql SQL clause to use for filtering, {@see set_field_sql}
* @param array $fieldparams
* @throws moodle_exception For invalid filter class
*/
public function __construct(
string $filterclass,
string $name,
lang_string $header,
string $entityname,
string $fieldsql = '',
array $fieldparams = []
) {
if (!class_exists($filterclass) || !is_subclass_of($filterclass, base::class)) {
throw new moodle_exception('filterinvalid', 'reportbuilder', '', null, $filterclass);
}
$this->filterclass = $filterclass;
$this->name = $name;
$this->header = $header;
$this->entityname = $entityname;
if ($fieldsql !== '') {
$this->set_field_sql($fieldsql, $fieldparams);
}
}
/**
* Get filter class path
*
* @return string
*/
public function get_filter_class(): string {
return $this->filterclass;
}
/**
* Get filter name
*
* @return string
*/
public function get_name(): string {
return $this->name;
}
/**
* Return header
*
* @return string
*/
public function get_header(): string {
return $this->header->out();
}
/**
* Set header
*
* @param lang_string $header
* @return self
*/
public function set_header(lang_string $header): self {
$this->header = $header;
return $this;
}
/**
* Return filter entity name
*
* @return string
*/
public function get_entity_name(): string {
return $this->entityname;
}
/**
* Return unique identifier for this filter
*
* @return string
*/
public function get_unique_identifier(): string {
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
*
* @return string
*/
public function get_field_sql(): string {
return $this->fieldsql;
}
/**
* Get the SQL params for the field being filtered
*
* @return array
*/
public function get_field_params(): array {
return $this->fieldparams;
}
/**
* Retrieve SQL expression and parameters for the field
*
* @param int $index
* @return array [$sql, [...$params]]
*/
public function get_field_sql_and_params(int $index = 0): array {
$fieldsql = $this->get_field_sql();
$fieldparams = $this->get_field_params();
// Shortcut if there aren't any parameters.
if (empty($fieldparams)) {
return [$fieldsql, $fieldparams];
}
// Simple callback for replacement of parameter names within filter SQL.
$transform = function(string $param) use ($index): string {
return "{$param}_{$index}";
};
$paramnames = array_keys($fieldparams);
$sql = database::sql_replace_parameter_names($fieldsql, $paramnames, $transform);
$params = [];
foreach ($paramnames as $paramname) {
$paramnametransform = $transform($paramname);
$params[$paramnametransform] = $fieldparams[$paramname];
}
return [$sql, $params];
}
/**
* Set the SQL expression for the field that is being filtered. It will be passed to the filter class
*
* @param string $sql
* @param array $params
* @return self
*/
public function set_field_sql(string $sql, array $params = []): self {
$this->fieldsql = $sql;
$this->fieldparams = $params;
return $this;
}
/**
* Return available state of the filter for the current user
*
* @return bool
*/
public function get_is_available(): bool {
return $this->available;
}
/**
* Conditionally set whether the filter is available. For instance the filter may be added to a report with the
* expectation that only some users are able to see it
*
* @param bool $available
* @return self
*/
public function set_is_available(bool $available): self {
$this->available = $available;
return $this;
}
/**
* Set deprecated state of the filter, in which case it will still be shown when already present in existing reports but
* won't be available for selection in the report editor
*
* @param string $deprecatedmessage
* @return self
*/
public function set_is_deprecated(string $deprecatedmessage = ''): self {
$this->deprecated = true;
$this->deprecatedmessage = $deprecatedmessage;
return $this;
}
/**
* Return deprecated state of the filter
*
* @return bool
*/
public function get_is_deprecated(): bool {
return $this->deprecated;
}
/**
* Return deprecated message of the filter
*
* @return string
*/
public function get_is_deprecated_message(): string {
return $this->deprecatedmessage;
}
/**
* Set the options for the filter in the format that the filter class expected (e.g. the "select" filter expects an array)
*
* This method should only be used if the options do not require any calculations/queries, in which
* case {@see set_options_callback} should be used. For performance, {@see get_string} shouldn't be used either, use of
* {@see lang_string} is instead encouraged
*
* @param mixed $options
* @return self
*/
public function set_options($options): self {
$this->options = $options;
return $this;
}
/**
* Set the options for the filter to be returned by a callback (that receives no arguments) in the format that the filter
* class expects
*
* @param callable $callback
* @return self
*/
public function set_options_callback(callable $callback): self {
$this->options = $callback;
return $this;
}
/**
* Get the options for the filter, returning via the the previously set options or generated via defined options callback
*
* @return mixed
*/
public function get_options() {
if (is_callable($this->options)) {
$callable = $this->options;
$this->options = ($callable)();
}
return $this->options;
}
/**
* Set a limited subset of operators that should be used for the filter, refer to each filter class to find defined
* operator constants
*
* @param array $limitoperators Simple array of operator values
* @return self
*/
public function set_limited_operators(array $limitoperators): self {
$this->limitoperators = $limitoperators;
return $this;
}
/**
* Filter given operators to include only those previously defined by {@see set_limited_operators}
*
* @param array $operators All operators as defined by the filter class
* @return array
*/
public function restrict_limited_operators(array $operators): array {
if (empty($this->limitoperators)) {
return $operators;
}
return array_intersect_key($operators, array_flip($this->limitoperators));
}
/**
* Set filter persistent
*
* @param filter_model $persistent
* @return self
*/
public function set_persistent(filter_model $persistent): self {
$this->persistent = $persistent;
return $this;
}
/**
* Return filter persistent
*
* @return filter_model|null
*/
public function get_persistent(): ?filter_model {
return $this->persistent ?? null;
}
}