forked from vergnet/site-accueil-insa
542 lines
15 KiB
PHP
542 lines
15 KiB
PHP
<?php
|
|
/**
|
|
* Matomo - free/libre analytics platform
|
|
*
|
|
* @link https://matomo.org
|
|
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
|
*
|
|
*/
|
|
namespace Piwik\DataTable;
|
|
|
|
use Closure;
|
|
use Piwik\Common;
|
|
use Piwik\DataTable;
|
|
use Piwik\DataTable\Renderer\Html;
|
|
|
|
/**
|
|
* Stores an array of {@link DataTable}s indexed by one type of {@link DataTable} metadata (such as site ID
|
|
* or period).
|
|
*
|
|
* DataTable Maps are returned on all queries that involve multiple sites and/or multiple
|
|
* periods. The Maps will contain a {@link DataTable} for each site and period combination.
|
|
*
|
|
* The Map implements some {@link DataTable} such as {@link queueFilter()} and {@link getRowsCount}.
|
|
*
|
|
*
|
|
* @api
|
|
*/
|
|
class Map implements DataTableInterface
|
|
{
|
|
/**
|
|
* Array containing the DataTable within this Set
|
|
*
|
|
* @var DataTable[]
|
|
*/
|
|
protected $array = array();
|
|
|
|
/**
|
|
* @see self::getKeyName()
|
|
* @var string
|
|
*/
|
|
protected $keyName = 'defaultKeyName';
|
|
|
|
/**
|
|
* Returns a string description of the data used to index the DataTables.
|
|
*
|
|
* This label is used by DataTable Renderers (it becomes a column name or the XML description tag).
|
|
*
|
|
* @return string eg, `'idSite'`, `'period'`
|
|
*/
|
|
public function getKeyName()
|
|
{
|
|
return $this->keyName;
|
|
}
|
|
|
|
/**
|
|
* Set the name of they metadata used to index {@link DataTable}s. See {@link getKeyName()}.
|
|
*
|
|
* @param string $name
|
|
*/
|
|
public function setKeyName($name)
|
|
{
|
|
$this->keyName = $name;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of {@link DataTable}s in this DataTable\Map.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function getRowsCount()
|
|
{
|
|
return count($this->getDataTables());
|
|
}
|
|
|
|
/**
|
|
* Queue a filter to {@link DataTable} child of contained by this instance.
|
|
*
|
|
* See {@link Piwik\DataTable::queueFilter()} for more information..
|
|
*
|
|
* @param string|Closure $className Filter name, eg. `'Limit'` or a Closure.
|
|
* @param array $parameters Filter parameters, eg. `array(50, 10)`.
|
|
*/
|
|
public function queueFilter($className, $parameters = array())
|
|
{
|
|
foreach ($this->getDataTables() as $table) {
|
|
$table->queueFilter($className, $parameters);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Apply the filters previously queued to each DataTable contained by this DataTable\Map.
|
|
*/
|
|
public function applyQueuedFilters()
|
|
{
|
|
foreach ($this->getDataTables() as $table) {
|
|
$table->applyQueuedFilters();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Apply a filter to all tables contained by this instance.
|
|
*
|
|
* @param string|Closure $className Name of filter class or a Closure.
|
|
* @param array $parameters Parameters to pass to the filter.
|
|
*/
|
|
public function filter($className, $parameters = array())
|
|
{
|
|
foreach ($this->getDataTables() as $table) {
|
|
$table->filter($className, $parameters);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Apply a filter to all subtables contained by this instance.
|
|
*
|
|
* @param string|Closure $className Name of filter class or a Closure.
|
|
* @param array $parameters Parameters to pass to the filter.
|
|
*/
|
|
public function filterSubtables($className, $parameters = array())
|
|
{
|
|
foreach ($this->getDataTables() as $table) {
|
|
$table->filterSubtables($className, $parameters);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Apply a queued filter to all subtables contained by this instance.
|
|
*
|
|
* @param string|Closure $className Name of filter class or a Closure.
|
|
* @param array $parameters Parameters to pass to the filter.
|
|
*/
|
|
public function queueFilterSubtables($className, $parameters = array())
|
|
{
|
|
foreach ($this->getDataTables() as $table) {
|
|
$table->queueFilterSubtables($className, $parameters);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the array of DataTables contained by this class.
|
|
*
|
|
* @return DataTable[]|Map[]
|
|
*/
|
|
public function getDataTables()
|
|
{
|
|
return $this->array;
|
|
}
|
|
|
|
/**
|
|
* Returns the table with the specific label.
|
|
*
|
|
* @param string $label
|
|
* @return DataTable|Map
|
|
*/
|
|
public function getTable($label)
|
|
{
|
|
return $this->array[$label];
|
|
}
|
|
|
|
/**
|
|
* @param string $label
|
|
* @return bool
|
|
*/
|
|
public function hasTable($label)
|
|
{
|
|
return isset($this->array[$label]);
|
|
}
|
|
|
|
/**
|
|
* Returns the first element in the Map's array.
|
|
*
|
|
* @return DataTable|Map|false
|
|
*/
|
|
public function getFirstRow()
|
|
{
|
|
return reset($this->array);
|
|
}
|
|
|
|
/**
|
|
* Returns the last element in the Map's array.
|
|
*
|
|
* @return DataTable|Map|false
|
|
*/
|
|
public function getLastRow()
|
|
{
|
|
return end($this->array);
|
|
}
|
|
|
|
/**
|
|
* Adds a new {@link DataTable} or Map instance to this DataTable\Map.
|
|
*
|
|
* @param DataTable|Map $table
|
|
* @param string $label Label used to index this table in the array.
|
|
*/
|
|
public function addTable($table, $label)
|
|
{
|
|
$this->array[$label] = $table;
|
|
}
|
|
|
|
public function getRowFromIdSubDataTable($idSubtable)
|
|
{
|
|
$dataTables = $this->getDataTables();
|
|
|
|
// find first datatable containing data
|
|
foreach ($dataTables as $subTable) {
|
|
$subTableRow = $subTable->getRowFromIdSubDataTable($idSubtable);
|
|
|
|
if (!empty($subTableRow)) {
|
|
return $subTableRow;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns a string output of this DataTable\Map (applying the default renderer to every {@link DataTable}
|
|
* of this DataTable\Map).
|
|
*
|
|
* @return string
|
|
*/
|
|
public function __toString()
|
|
{
|
|
$renderer = new Html();
|
|
$renderer->setTable($this);
|
|
return (string)$renderer;
|
|
}
|
|
|
|
/**
|
|
* See {@link DataTable::enableRecursiveSort()}.
|
|
*/
|
|
public function enableRecursiveSort()
|
|
{
|
|
foreach ($this->getDataTables() as $table) {
|
|
$table->enableRecursiveSort();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* See {@link DataTable::disableFilter()}.
|
|
*/
|
|
public function disableFilter($className)
|
|
{
|
|
foreach ($this->getDataTables() as $table) {
|
|
$table->disableFilter($className);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @ignore
|
|
*/
|
|
public function disableRecursiveFilters()
|
|
{
|
|
foreach ($this->getDataTables() as $table) {
|
|
$table->disableRecursiveFilters();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @ignore
|
|
*/
|
|
public function enableRecursiveFilters()
|
|
{
|
|
foreach ($this->getDataTables() as $table) {
|
|
$table->enableRecursiveFilters();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Renames the given column in each contained {@link DataTable}.
|
|
*
|
|
* See {@link DataTable::renameColumn()}.
|
|
*
|
|
* @param string $oldName
|
|
* @param string $newName
|
|
*/
|
|
public function renameColumn($oldName, $newName)
|
|
{
|
|
foreach ($this->getDataTables() as $table) {
|
|
$table->renameColumn($oldName, $newName);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes the specified columns in each contained {@link DataTable}.
|
|
*
|
|
* See {@link DataTable::deleteColumns()}.
|
|
*
|
|
* @param array $columns The columns to delete.
|
|
* @param bool $deleteRecursiveInSubtables This param is currently not used.
|
|
*/
|
|
public function deleteColumns($columns, $deleteRecursiveInSubtables = false)
|
|
{
|
|
foreach ($this->getDataTables() as $table) {
|
|
$table->deleteColumns($columns);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes a table from the array of DataTables.
|
|
*
|
|
* @param string $id The label associated with {@link DataTable}.
|
|
*/
|
|
public function deleteRow($id)
|
|
{
|
|
unset($this->array[$id]);
|
|
}
|
|
|
|
/**
|
|
* Deletes the given column in every contained {@link DataTable}.
|
|
*
|
|
* @see DataTable::deleteColumn
|
|
* @param string $name
|
|
*/
|
|
public function deleteColumn($name)
|
|
{
|
|
foreach ($this->getDataTables() as $table) {
|
|
$table->deleteColumn($name);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the array containing all column values in all contained {@link DataTable}s for the requested column.
|
|
*
|
|
* @param string $name The column name.
|
|
* @return array
|
|
*/
|
|
public function getColumn($name)
|
|
{
|
|
$values = array();
|
|
|
|
foreach ($this->getDataTables() as $table) {
|
|
$moreValues = $table->getColumn($name);
|
|
foreach ($moreValues as &$value) {
|
|
$values[] = $value;
|
|
}
|
|
}
|
|
|
|
return $values;
|
|
}
|
|
|
|
/**
|
|
* Merges the rows of every child {@link DataTable} into a new one and
|
|
* returns it. This function will also set the label of the merged rows
|
|
* to the label of the {@link DataTable} they were originally from.
|
|
*
|
|
* The result of this function is determined by the type of DataTable
|
|
* this instance holds. If this DataTable\Map instance holds an array
|
|
* of DataTables, this function will transform it from:
|
|
*
|
|
* Label 0:
|
|
* DataTable(row1)
|
|
* Label 1:
|
|
* DataTable(row2)
|
|
*
|
|
* to:
|
|
*
|
|
* DataTable(row1[label = 'Label 0'], row2[label = 'Label 1'])
|
|
*
|
|
* If this instance holds an array of DataTable\Maps, this function will
|
|
* transform it from:
|
|
*
|
|
* Outer Label 0: // the outer DataTable\Map
|
|
* Inner Label 0: // one of the inner DataTable\Maps
|
|
* DataTable(row1)
|
|
* Inner Label 1:
|
|
* DataTable(row2)
|
|
* Outer Label 1:
|
|
* Inner Label 0:
|
|
* DataTable(row3)
|
|
* Inner Label 1:
|
|
* DataTable(row4)
|
|
*
|
|
* to:
|
|
*
|
|
* Inner Label 0:
|
|
* DataTable(row1[label = 'Outer Label 0'], row3[label = 'Outer Label 1'])
|
|
* Inner Label 1:
|
|
* DataTable(row2[label = 'Outer Label 0'], row4[label = 'Outer Label 1'])
|
|
*
|
|
* If this instance holds an array of DataTable\Maps, the
|
|
* metadata of the first child is used as the metadata of the result.
|
|
*
|
|
* This function can be used, for example, to smoosh IndexedBySite archive
|
|
* query results into one DataTable w/ different rows differentiated by site ID.
|
|
*
|
|
* Note: This DataTable/Map will be destroyed and will be no longer usable after the tables have been merged into
|
|
* the new dataTable to reduce memory usage. Destroying all DataTables within the Map also seems to fix a
|
|
* Segmentation Fault that occurred in the AllWebsitesDashboard when having > 16k sites.
|
|
*
|
|
* @return DataTable|Map
|
|
*/
|
|
public function mergeChildren()
|
|
{
|
|
$firstChild = reset($this->array);
|
|
|
|
if ($firstChild instanceof Map) {
|
|
$result = $firstChild->getEmptyClone();
|
|
|
|
/** @var $subDataTableMap Map */
|
|
foreach ($this->getDataTables() as $label => $subDataTableMap) {
|
|
foreach ($subDataTableMap->getDataTables() as $innerLabel => $subTable) {
|
|
if (!isset($result->array[$innerLabel])) {
|
|
$dataTable = new DataTable();
|
|
$dataTable->setMetadataValues($subTable->getAllTableMetadata());
|
|
|
|
$result->addTable($dataTable, $innerLabel);
|
|
}
|
|
|
|
$this->copyRowsAndSetLabel($result->array[$innerLabel], $subTable, $label);
|
|
}
|
|
}
|
|
} else {
|
|
$result = new DataTable();
|
|
|
|
foreach ($this->getDataTables() as $label => $subTable) {
|
|
$this->copyRowsAndSetLabel($result, $subTable, $label);
|
|
Common::destroy($subTable);
|
|
}
|
|
|
|
$this->array = array();
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Utility function used by mergeChildren. Copies the rows from one table,
|
|
* sets their 'label' columns to a value and adds them to another table.
|
|
*
|
|
* @param DataTable $toTable The table to copy rows to.
|
|
* @param DataTable $fromTable The table to copy rows from.
|
|
* @param string $label The value to set the 'label' column of every copied row.
|
|
*/
|
|
private function copyRowsAndSetLabel($toTable, $fromTable, $label)
|
|
{
|
|
foreach ($fromTable->getRows() as $fromRow) {
|
|
$oldColumns = $fromRow->getColumns();
|
|
unset($oldColumns['label']);
|
|
|
|
$columns = array_merge(array('label' => $label), $oldColumns);
|
|
$row = new Row(array(
|
|
Row::COLUMNS => $columns,
|
|
Row::METADATA => $fromRow->getMetadata(),
|
|
Row::DATATABLE_ASSOCIATED => $fromRow->getIdSubDataTable()
|
|
));
|
|
$toTable->addRow($row);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sums a DataTable to all the tables in this array.
|
|
*
|
|
* _Note: Will only add `$tableToSum` if the childTable has some rows._
|
|
*
|
|
* See {@link Piwik\DataTable::addDataTable()}.
|
|
*
|
|
* @param DataTable $tableToSum
|
|
*/
|
|
public function addDataTable(DataTable $tableToSum)
|
|
{
|
|
foreach ($this->getDataTables() as $childTable) {
|
|
$childTable->addDataTable($tableToSum);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a new DataTable\Map w/ child tables that have had their
|
|
* subtables merged.
|
|
*
|
|
* See {@link DataTable::mergeSubtables()}.
|
|
*
|
|
* @return Map
|
|
*/
|
|
public function mergeSubtables()
|
|
{
|
|
$result = $this->getEmptyClone();
|
|
foreach ($this->getDataTables() as $label => $childTable) {
|
|
$result->addTable($childTable->mergeSubtables(), $label);
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Returns a new DataTable\Map w/o any child DataTables, but with
|
|
* the same key name as this instance.
|
|
*
|
|
* @return Map
|
|
*/
|
|
public function getEmptyClone()
|
|
{
|
|
$dataTableMap = new Map;
|
|
$dataTableMap->setKeyName($this->getKeyName());
|
|
return $dataTableMap;
|
|
}
|
|
|
|
/**
|
|
* Returns the intersection of children's metadata arrays (what they all have in common).
|
|
*
|
|
* @param string $name The metadata name.
|
|
* @return mixed
|
|
*/
|
|
public function getMetadataIntersectArray($name)
|
|
{
|
|
$data = array();
|
|
foreach ($this->getDataTables() as $childTable) {
|
|
$childData = $childTable->getMetadata($name);
|
|
if (is_array($childData)) {
|
|
$data = array_intersect($data, $childData);
|
|
}
|
|
}
|
|
return array_values($data);
|
|
}
|
|
|
|
/**
|
|
* Delete row metadata by name in every row.
|
|
*
|
|
* @param $name
|
|
* @param bool $deleteRecursiveInSubtables
|
|
*/
|
|
public function deleteRowsMetadata($name, $deleteRecursiveInSubtables = false)
|
|
{
|
|
foreach ($this->getDataTables() as $table) {
|
|
$table->deleteRowsMetadata($name, $deleteRecursiveInSubtables);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* See {@link DataTable::getColumns()}.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getColumns()
|
|
{
|
|
foreach ($this->getDataTables() as $childTable) {
|
|
if ($childTable->getRowsCount() > 0) {
|
|
return $childTable->getColumns();
|
|
}
|
|
}
|
|
return array();
|
|
}
|
|
}
|