1<?php 2/** 3 * Matomo - free/libre analytics platform 4 * 5 * @link https://matomo.org 6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later 7 * 8 */ 9namespace Piwik\DataTable\Filter; 10 11use Piwik\DataTable\BaseFilter; 12use Piwik\DataTable\Simple; 13use Piwik\DataTable; 14use Piwik\Metrics\Sorter; 15 16/** 17 * Sorts a {@link DataTable} based on the value of a specific column. 18 * 19 * It is possible to specify a natural sorting (see [php.net/natsort](http://php.net/natsort) for details). 20 * 21 * @api 22 */ 23class Sort extends BaseFilter 24{ 25 protected $columnToSort; 26 protected $order; 27 protected $naturalSort; 28 protected $isSecondaryColumnSortEnabled; 29 protected $secondaryColumnSortCallback; 30 31 const ORDER_DESC = 'desc'; 32 const ORDER_ASC = 'asc'; 33 34 /** 35 * Constructor. 36 * 37 * @param DataTable $table The table to eventually filter. 38 * @param string $columnToSort The name of the column to sort by. 39 * @param string $order order `'asc'` or `'desc'`. 40 * @param bool $naturalSort Whether to use a natural sort or not (see {@link http://php.net/natsort}). 41 * @param bool $recursiveSort Whether to sort all subtables or not. 42 * @param bool|callback $doSortBySecondaryColumn If true will sort by a secondary column. The column is automatically 43 * detected and will be either nb_visits or label, if possible. 44 * If callback given it will sort by the column returned by the callback (if any) 45 * callback will be called with 2 parameters: primaryColumnToSort and table 46 */ 47 public function __construct($table, $columnToSort, $order = 'desc', $naturalSort = true, $recursiveSort = true, $doSortBySecondaryColumn = false) 48 { 49 parent::__construct($table); 50 51 if ($recursiveSort) { 52 $table->enableRecursiveSort(); 53 } 54 55 $this->columnToSort = $columnToSort; 56 $this->naturalSort = $naturalSort; 57 $this->order = strtolower($order); 58 $this->isSecondaryColumnSortEnabled = !empty($doSortBySecondaryColumn); 59 $this->secondaryColumnSortCallback = is_callable($doSortBySecondaryColumn) ? $doSortBySecondaryColumn : null; 60 } 61 62 /** 63 * See {@link Sort}. 64 * 65 * @param DataTable $table 66 * @return mixed 67 */ 68 public function filter($table) 69 { 70 if ($table instanceof Simple) { 71 return; 72 } 73 74 if (empty($this->columnToSort)) { 75 return; 76 } 77 78 if (!$table->getRowsCountWithoutSummaryRow()) { 79 return; 80 } 81 82 $row = $table->getFirstRow(); 83 84 if ($row === false) { 85 return; 86 } 87 88 $config = new Sorter\Config(); 89 $sorter = new Sorter($config); 90 91 $config->naturalSort = $this->naturalSort; 92 $config->primaryColumnToSort = $sorter->getPrimaryColumnToSort($table, $this->columnToSort); 93 $config->primarySortOrder = $sorter->getPrimarySortOrder($this->order); 94 $config->primarySortFlags = $sorter->getBestSortFlags($table, $config->primaryColumnToSort); 95 if (!empty($this->secondaryColumnSortCallback)) { 96 $config->secondaryColumnToSort = call_user_func($this->secondaryColumnSortCallback, $config->primaryColumnToSort, $table); 97 } else { 98 $config->secondaryColumnToSort = $sorter->getSecondaryColumnToSort($row, $config->primaryColumnToSort); 99 } 100 $config->secondarySortOrder = $sorter->getSecondarySortOrder($this->order, $config->secondaryColumnToSort); 101 $config->secondarySortFlags = $sorter->getBestSortFlags($table, $config->secondaryColumnToSort); 102 103 // secondary sort should not be needed for all other sort flags (eg string/natural sort) as label is unique and would make it slower 104 $isSecondaryColumnSortNeeded = $config->primarySortFlags === SORT_NUMERIC; 105 $config->isSecondaryColumnSortEnabled = $this->isSecondaryColumnSortEnabled && $isSecondaryColumnSortNeeded; 106 107 $this->sort($sorter, $table); 108 } 109 110 private function sort(Sorter $sorter, DataTable $table) 111 { 112 $sorter->sort($table); 113 114 if ($table->isSortRecursiveEnabled()) { 115 foreach ($table->getRowsWithoutSummaryRow() as $row) { 116 $subTable = $row->getSubtable(); 117 118 if ($subTable) { 119 $subTable->enableRecursiveSort(); 120 $this->sort($sorter, $subTable); 121 } 122 } 123 } 124 } 125 126}