xref: /plugin/struct/meta/NestedResult.php (revision ce44c6393fcd559a7f07942c55824e5b8379912c)
15bc00e11SAndreas Gohr<?php
25bc00e11SAndreas Gohr
35bc00e11SAndreas Gohrnamespace dokuwiki\plugin\struct\meta;
45bc00e11SAndreas Gohr
5*ce44c639SAndreas Gohruse dokuwiki\plugin\struct\types\Text;
6*ce44c639SAndreas Gohruse dokuwiki\Utf8\PhpString;
7*ce44c639SAndreas Gohr
85bc00e11SAndreas Gohr/**
95bc00e11SAndreas Gohr * This class builds a nested tree from a search result
105bc00e11SAndreas Gohr *
115bc00e11SAndreas Gohr * This is used to create the nested output in the AggregationList
125bc00e11SAndreas Gohr */
135bc00e11SAndreas Gohrclass NestedResult
145bc00e11SAndreas Gohr{
155bc00e11SAndreas Gohr
165bc00e11SAndreas Gohr    /** @var NestedValue[] */
175bc00e11SAndreas Gohr    protected $nodes = [];
185bc00e11SAndreas Gohr
19*ce44c639SAndreas Gohr    /** @var NestedValue[] */
20*ce44c639SAndreas Gohr    protected $indexNodes = [];
21*ce44c639SAndreas Gohr
225bc00e11SAndreas Gohr    /** @var Value[][] */
235bc00e11SAndreas Gohr    protected $result;
245bc00e11SAndreas Gohr
255bc00e11SAndreas Gohr    /**
265bc00e11SAndreas Gohr     * @param Value[][] $result the original search result
275bc00e11SAndreas Gohr     * @return void
285bc00e11SAndreas Gohr     */
295bc00e11SAndreas Gohr    public function __construct($result)
305bc00e11SAndreas Gohr    {
315bc00e11SAndreas Gohr        $this->result = $result;
325bc00e11SAndreas Gohr    }
335bc00e11SAndreas Gohr
345bc00e11SAndreas Gohr    /**
355bc00e11SAndreas Gohr     * Get the nested result
365bc00e11SAndreas Gohr     *
375bc00e11SAndreas Gohr     * @param int $nesting the nesting level to use
38*ce44c639SAndreas Gohr     * @param int $index the number of characters to use for indexing
395bc00e11SAndreas Gohr     * @return NestedValue the root node of the nested tree
405bc00e11SAndreas Gohr     */
41*ce44c639SAndreas Gohr    public function getRoot($nesting, $index = 0)
42*ce44c639SAndreas Gohr    {
435bc00e11SAndreas Gohr        $this->nodes = [];
445bc00e11SAndreas Gohr        $root = new NestedValue(null, -1);
455bc00e11SAndreas Gohr
465bc00e11SAndreas Gohr        if (!$this->result) return $root;
47*ce44c639SAndreas Gohr
485bc00e11SAndreas Gohr        foreach ($this->result as $row) {
495bc00e11SAndreas Gohr            $this->nestBranch($root, $row, $nesting);
505bc00e11SAndreas Gohr        }
515bc00e11SAndreas Gohr
52*ce44c639SAndreas Gohr        $root = $this->createIndex($root, $index);
535bc00e11SAndreas Gohr        return $root;
545bc00e11SAndreas Gohr    }
555bc00e11SAndreas Gohr
565bc00e11SAndreas Gohr    /**
57*ce44c639SAndreas Gohr     * Add a top level index to the tree
58*ce44c639SAndreas Gohr     *
59*ce44c639SAndreas Gohr     * @param NestedValue $root Root node of the tree
60*ce44c639SAndreas Gohr     * @param int $index Number of characters to use for indexing
61*ce44c639SAndreas Gohr     * @return NestedValue new root node
62*ce44c639SAndreas Gohr     */
63*ce44c639SAndreas Gohr    protected function createIndex(NestedValue $root, $index)
64*ce44c639SAndreas Gohr    {
65*ce44c639SAndreas Gohr        if (!$index) return $root;
66*ce44c639SAndreas Gohr        $this->indexNodes = [];
67*ce44c639SAndreas Gohr
68*ce44c639SAndreas Gohr        $children = $root->getChildren();
69*ce44c639SAndreas Gohr        $resultRows = $root->getResultRows();
70*ce44c639SAndreas Gohr        if ($children) {
71*ce44c639SAndreas Gohr            // there are children, so we are a nested result
72*ce44c639SAndreas Gohr            foreach ($children as $child) {
73*ce44c639SAndreas Gohr                $indexValue = $child->getValueObject();
74*ce44c639SAndreas Gohr                $indexNode = $this->getIndexNode($indexValue, $index);
75*ce44c639SAndreas Gohr                $indexNode->addChild($child);
76*ce44c639SAndreas Gohr                $child->setDepth(1); // increase child's depth from 0 to 1
77*ce44c639SAndreas Gohr            }
78*ce44c639SAndreas Gohr        } elseif ($resultRows) {
79*ce44c639SAndreas Gohr            // no children, so we are currently a flat result
80*ce44c639SAndreas Gohr            foreach ($resultRows as $row) {
81*ce44c639SAndreas Gohr                $indexValue = $row[0];
82*ce44c639SAndreas Gohr                $indexNode = $this->getIndexNode($indexValue, $index);
83*ce44c639SAndreas Gohr                $indexNode->addResultRow($row);
84*ce44c639SAndreas Gohr            }
85*ce44c639SAndreas Gohr        }
86*ce44c639SAndreas Gohr
87*ce44c639SAndreas Gohr        // now all results are added to index nodes - use them as children
88*ce44c639SAndreas Gohr        $newRoot = new NestedValue(null, -1);
89*ce44c639SAndreas Gohr        foreach ($this->indexNodes as $node) {
90*ce44c639SAndreas Gohr            $newRoot->addChild($node);
91*ce44c639SAndreas Gohr        }
92*ce44c639SAndreas Gohr        return $newRoot;
93*ce44c639SAndreas Gohr    }
94*ce44c639SAndreas Gohr
95*ce44c639SAndreas Gohr    /**
965bc00e11SAndreas Gohr     * Creates nested nodes for a given result row
975bc00e11SAndreas Gohr     *
985bc00e11SAndreas Gohr     * Splits up multi Values into separate nodes, when used in nesting
995bc00e11SAndreas Gohr     *
1005bc00e11SAndreas Gohr     * @param Value[] $row current result row to work on
1015bc00e11SAndreas Gohr     * @param int $nesting number of wanted nesting levels
1025bc00e11SAndreas Gohr     * @param int $depth current nesting depth (used in recursion)
1035bc00e11SAndreas Gohr     */
1045bc00e11SAndreas Gohr    protected function nestBranch(NestedValue $parent, $row, $nesting, $depth = 0)
1055bc00e11SAndreas Gohr    {
1065bc00e11SAndreas Gohr        // nesting level reached, add row and return
1075bc00e11SAndreas Gohr        if ($depth >= $nesting) {
1085bc00e11SAndreas Gohr            $parent->addResultRow($row);
1095bc00e11SAndreas Gohr            return;
1105bc00e11SAndreas Gohr        }
1115bc00e11SAndreas Gohr
1125bc00e11SAndreas Gohr        $valObj = array_shift($row);
1135bc00e11SAndreas Gohr        if (!$valObj) return; // no more values to nest, usually shouldn't happen
1145bc00e11SAndreas Gohr
1155bc00e11SAndreas Gohr        if ($valObj->getColumn()->isMulti()) {
1165bc00e11SAndreas Gohr            // split up multi values into separate nodes
1175bc00e11SAndreas Gohr            $values = $valObj->getValue();
1185bc00e11SAndreas Gohr            foreach ($values as $value) {
1195bc00e11SAndreas Gohr                $newValue = new Value($valObj->getColumn(), $value);
1205bc00e11SAndreas Gohr                $node = $this->getNodeForValue($newValue, $depth);
1215bc00e11SAndreas Gohr                $parent->addChild($node);
1225bc00e11SAndreas Gohr                $this->nestBranch($node, $row, $nesting, $depth + 1);
1235bc00e11SAndreas Gohr            }
1245bc00e11SAndreas Gohr        } else {
1255bc00e11SAndreas Gohr            $node = $this->getNodeForValue($valObj, $depth);
1265bc00e11SAndreas Gohr            $parent->addChild($node);
1275bc00e11SAndreas Gohr            $this->nestBranch($node, $row, $nesting, $depth + 1);
1285bc00e11SAndreas Gohr
1295bc00e11SAndreas Gohr        }
1305bc00e11SAndreas Gohr    }
1315bc00e11SAndreas Gohr
1325bc00e11SAndreas Gohr    /**
1335bc00e11SAndreas Gohr     * Create or get existing Node from the tree
1345bc00e11SAndreas Gohr     *
1355bc00e11SAndreas Gohr     * @param Value $value
1365bc00e11SAndreas Gohr     * @param int $depth
1375bc00e11SAndreas Gohr     * @return NestedValue
1385bc00e11SAndreas Gohr     */
1395bc00e11SAndreas Gohr    protected function getNodeForValue(Value $value, $depth)
1405bc00e11SAndreas Gohr    {
1415bc00e11SAndreas Gohr        $node = new NestedValue($value, $depth);
1425bc00e11SAndreas Gohr        $key = (string)$node;
1435bc00e11SAndreas Gohr        if (!isset($this->nodes[$key])) {
1445bc00e11SAndreas Gohr            $this->nodes[$key] = $node;
1455bc00e11SAndreas Gohr        }
1465bc00e11SAndreas Gohr        return $this->nodes[$key];
1475bc00e11SAndreas Gohr    }
148*ce44c639SAndreas Gohr
149*ce44c639SAndreas Gohr    /**
150*ce44c639SAndreas Gohr     * Create or get an existing Node for indexing
151*ce44c639SAndreas Gohr     *
152*ce44c639SAndreas Gohr     * @param Value $value
153*ce44c639SAndreas Gohr     * @param int $index
154*ce44c639SAndreas Gohr     * @return NestedValue
155*ce44c639SAndreas Gohr     */
156*ce44c639SAndreas Gohr    protected function getIndexNode(Value $value, $index)
157*ce44c639SAndreas Gohr    {
158*ce44c639SAndreas Gohr        $compare = $value->getCompareValue();
159*ce44c639SAndreas Gohr        if (is_array($compare)) $compare = $compare[0];
160*ce44c639SAndreas Gohr        $key = PhpString::strtoupper(PhpString::substr($compare, 0, $index));
161*ce44c639SAndreas Gohr
162*ce44c639SAndreas Gohr        if (!isset($this->indexNodes[$key])) {
163*ce44c639SAndreas Gohr            $col = new Column(
164*ce44c639SAndreas Gohr                0,
165*ce44c639SAndreas Gohr                new Text([], '%index%', false),
166*ce44c639SAndreas Gohr                -1,
167*ce44c639SAndreas Gohr                true,
168*ce44c639SAndreas Gohr                $value->getColumn()->getTable()
169*ce44c639SAndreas Gohr            );
170*ce44c639SAndreas Gohr            $this->indexNodes[$key] = new NestedValue(new Value($col, $key), 0);
171*ce44c639SAndreas Gohr        }
172*ce44c639SAndreas Gohr
173*ce44c639SAndreas Gohr        return $this->indexNodes[$key];
174*ce44c639SAndreas Gohr    }
1755bc00e11SAndreas Gohr}
1765bc00e11SAndreas Gohr
1775bc00e11SAndreas Gohr
178