File: NestedSitesLimiter.php

package info (click to toggle)
matomo 5.8.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 95,068 kB
  • sloc: php: 289,425; xml: 127,249; javascript: 112,130; python: 202; sh: 178; makefile: 20; sql: 10
file content (135 lines) | stat: -rw-r--r-- 3,478 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
<?php

/**
 * Matomo - free/libre analytics platform
 *
 * @link    https://matomo.org
 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 */

namespace Piwik\Plugins\MultiSites\DataTable\Filter;

use Piwik\DataTable\BaseFilter;
use Piwik\DataTable\Row;
use Piwik\DataTable;

/**
 * Makes sure to not have any subtables anymore and applies the limit to the flattened table.
 *
 * So if $table is
 * array(
 *    site1
 *    group2
 *        subtable => site3
 *                    site4
 *                    site5
 *    site6
 *    site7
 * )
 *
 * it will format this to
 *
 * array(
 *    site1
 *    group2
 *    site3
 *    site4
 *    site5
 *    site6
 *    site7
 * )
 *
 * and then apply the limit filter.
 *
 * Each group will not count into the limit/offset. This way, if one requests a limit of 50 sites,
 * we make sure to return 50 sites.
 *
 * @param $sites
 * @return array
 */
class NestedSitesLimiter extends BaseFilter
{
    /** @var int */
    private $offset = 0;
    /** @var int */
    private $limit  = 0;
    /** @var Row[] */
    private $rows   = [];

    /**
     * Constructor.
     *
     * @param DataTable $table The table to eventually filter.
     */
    public function __construct(DataTable $table, int $offset, int $limit)
    {
        parent::__construct($table);
        $this->offset = $offset;
        $this->limit  = $limit;
    }

    /**
     * @param DataTable $table
     */
    public function filter($table)
    {
        $numRows = 0;
        $lastGroupFromPreviousPage = null;

        foreach ($table->getRows() as $row) {
            $this->addRowIfNeeded($row, $numRows);
            $numRows++;

            $subtable = $row->getSubtable();
            if ($subtable) {
                if (!$this->hasRows()) {
                    $lastGroupFromPreviousPage = $row;
                }
                foreach ($subtable->getRows() as $subRow) {
                    $this->addRowIfNeeded($subRow, $numRows);
                    $numRows++;
                }
                $row->removeSubtable();
            }

            if ($this->hasNumberOfRequestedRowsFound()) {
                break;
            }
        }

        $this->prependGroupIfFirstSiteBelongsToAGroupButGroupIsMissingInRows($lastGroupFromPreviousPage);

        $table->setRows($this->rows);
    }

    private function hasNumberOfRequestedRowsFound(): bool
    {
        return count($this->rows) >= $this->limit;
    }

    private function hasRows(): bool
    {
        return count($this->rows) !== 0;
    }

    private function addRowIfNeeded(Row $row, int $numRows): void
    {
        $inOffset = $numRows >= $this->offset;

        if ($inOffset && !$this->hasNumberOfRequestedRowsFound()) {
            $this->rows[] = $row;
        }
    }

    private function prependGroupIfFirstSiteBelongsToAGroupButGroupIsMissingInRows(?Row $lastGroupFromPreviousPage): void
    {
        if ($lastGroupFromPreviousPage && !empty($this->rows)) {
            // the result starts with a row that does belong to a group, we make sure to show this group before that site
            $group = reset($this->rows)->getMetadata('group');
            if ($group && $lastGroupFromPreviousPage->getColumn('label') === $group) {
                array_unshift($this->rows, $lastGroupFromPreviousPage);
                // we do not remove the last item as it could result in errors, instead we show $limit+1 entries
            }
        }
    }
}