15bc00e11SAndreas Gohr<?php 25bc00e11SAndreas Gohr 35bc00e11SAndreas Gohrnamespace dokuwiki\plugin\struct\meta; 45bc00e11SAndreas Gohr 5ce44c639SAndreas Gohruse dokuwiki\plugin\struct\types\Text; 6ce44c639SAndreas Gohruse dokuwiki\Utf8\PhpString; 7ce44c639SAndreas 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 /** @var NestedValue[] */ 165bc00e11SAndreas Gohr protected $nodes = []; 175bc00e11SAndreas Gohr 18ce44c639SAndreas Gohr /** @var NestedValue[] */ 19ce44c639SAndreas Gohr protected $indexNodes = []; 20ce44c639SAndreas Gohr 215bc00e11SAndreas Gohr /** @var Value[][] */ 225bc00e11SAndreas Gohr protected $result; 235bc00e11SAndreas Gohr 245bc00e11SAndreas Gohr /** 255bc00e11SAndreas Gohr * @param Value[][] $result the original search result 265bc00e11SAndreas Gohr * @return void 275bc00e11SAndreas Gohr */ 285bc00e11SAndreas Gohr public function __construct($result) 295bc00e11SAndreas Gohr { 305bc00e11SAndreas Gohr $this->result = $result; 315bc00e11SAndreas Gohr } 325bc00e11SAndreas Gohr 335bc00e11SAndreas Gohr /** 345bc00e11SAndreas Gohr * Get the nested result 355bc00e11SAndreas Gohr * 365bc00e11SAndreas Gohr * @param int $nesting the nesting level to use 37ce44c639SAndreas Gohr * @param int $index the number of characters to use for indexing 385bc00e11SAndreas Gohr * @return NestedValue the root node of the nested tree 395bc00e11SAndreas Gohr */ 40ce44c639SAndreas Gohr public function getRoot($nesting, $index = 0) 41ce44c639SAndreas Gohr { 425bc00e11SAndreas Gohr $this->nodes = []; 435bc00e11SAndreas Gohr $root = new NestedValue(null, -1); 445bc00e11SAndreas Gohr 455bc00e11SAndreas Gohr if (!$this->result) return $root; 46ce44c639SAndreas Gohr 475bc00e11SAndreas Gohr foreach ($this->result as $row) { 485bc00e11SAndreas Gohr $this->nestBranch($root, $row, $nesting); 495bc00e11SAndreas Gohr } 505bc00e11SAndreas Gohr 51ce44c639SAndreas Gohr $root = $this->createIndex($root, $index); 525bc00e11SAndreas Gohr return $root; 535bc00e11SAndreas Gohr } 545bc00e11SAndreas Gohr 555bc00e11SAndreas Gohr /** 56ce44c639SAndreas Gohr * Add a top level index to the tree 57ce44c639SAndreas Gohr * 58ce44c639SAndreas Gohr * @param NestedValue $root Root node of the tree 59ce44c639SAndreas Gohr * @param int $index Number of characters to use for indexing 60ce44c639SAndreas Gohr * @return NestedValue new root node 61ce44c639SAndreas Gohr */ 62ce44c639SAndreas Gohr protected function createIndex(NestedValue $root, $index) 63ce44c639SAndreas Gohr { 64ce44c639SAndreas Gohr if (!$index) return $root; 65ce44c639SAndreas Gohr $this->indexNodes = []; 66ce44c639SAndreas Gohr 67ce44c639SAndreas Gohr $children = $root->getChildren(); 68ce44c639SAndreas Gohr $resultRows = $root->getResultRows(); 69ce44c639SAndreas Gohr if ($children) { 70ce44c639SAndreas Gohr // there are children, so we are a nested result 71ce44c639SAndreas Gohr foreach ($children as $child) { 72ce44c639SAndreas Gohr $indexValue = $child->getValueObject(); 73ce44c639SAndreas Gohr $indexNode = $this->getIndexNode($indexValue, $index); 74ce44c639SAndreas Gohr $indexNode->addChild($child); 75ce44c639SAndreas Gohr $child->setDepth(1); // increase child's depth from 0 to 1 76ce44c639SAndreas Gohr } 77ce44c639SAndreas Gohr } elseif ($resultRows) { 78ce44c639SAndreas Gohr // no children, so we are currently a flat result 79ce44c639SAndreas Gohr foreach ($resultRows as $row) { 80ce44c639SAndreas Gohr $indexValue = $row[0]; 81ce44c639SAndreas Gohr $indexNode = $this->getIndexNode($indexValue, $index); 82ce44c639SAndreas Gohr $indexNode->addResultRow($row); 83ce44c639SAndreas Gohr } 84ce44c639SAndreas Gohr } 85ce44c639SAndreas Gohr 86ce44c639SAndreas Gohr // now all results are added to index nodes - use them as children 87ce44c639SAndreas Gohr $newRoot = new NestedValue(null, -1); 88ce44c639SAndreas Gohr foreach ($this->indexNodes as $node) { 89ce44c639SAndreas Gohr $newRoot->addChild($node); 90ce44c639SAndreas Gohr } 91ce44c639SAndreas Gohr return $newRoot; 92ce44c639SAndreas Gohr } 93ce44c639SAndreas Gohr 94ce44c639SAndreas Gohr /** 955bc00e11SAndreas Gohr * Creates nested nodes for a given result row 965bc00e11SAndreas Gohr * 975bc00e11SAndreas Gohr * Splits up multi Values into separate nodes, when used in nesting 985bc00e11SAndreas Gohr * 995bc00e11SAndreas Gohr * @param Value[] $row current result row to work on 1005bc00e11SAndreas Gohr * @param int $nesting number of wanted nesting levels 1015bc00e11SAndreas Gohr * @param int $depth current nesting depth (used in recursion) 1025bc00e11SAndreas Gohr */ 1035bc00e11SAndreas Gohr protected function nestBranch(NestedValue $parent, $row, $nesting, $depth = 0) 1045bc00e11SAndreas Gohr { 1055bc00e11SAndreas Gohr // nesting level reached, add row and return 1065bc00e11SAndreas Gohr if ($depth >= $nesting) { 1075bc00e11SAndreas Gohr $parent->addResultRow($row); 1085bc00e11SAndreas Gohr return; 1095bc00e11SAndreas Gohr } 1105bc00e11SAndreas Gohr 1115bc00e11SAndreas Gohr $valObj = array_shift($row); 1125bc00e11SAndreas Gohr if (!$valObj) return; // no more values to nest, usually shouldn't happen 1135bc00e11SAndreas Gohr 114*7b7a9290SAndreas Gohr if ($valObj->getColumn()->isMulti() && $valObj->getValue()) { 1155bc00e11SAndreas Gohr // split up multi values into separate nodes 1165bc00e11SAndreas Gohr $values = $valObj->getValue(); 117*7b7a9290SAndreas Gohr if($values) { 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 { 125*7b7a9290SAndreas Gohr $newValue = new Value($valObj->getColumn(), ''); // add empty node 126*7b7a9290SAndreas Gohr $node = $this->getNodeForValue($newValue, $depth); 127*7b7a9290SAndreas Gohr $parent->addChild($node); 128*7b7a9290SAndreas Gohr $this->nestBranch($node, $row, $nesting, $depth + 1); 129*7b7a9290SAndreas Gohr } 130*7b7a9290SAndreas Gohr } else { 1315bc00e11SAndreas Gohr $node = $this->getNodeForValue($valObj, $depth); 1325bc00e11SAndreas Gohr $parent->addChild($node); 1335bc00e11SAndreas Gohr $this->nestBranch($node, $row, $nesting, $depth + 1); 1345bc00e11SAndreas Gohr } 1355bc00e11SAndreas Gohr } 1365bc00e11SAndreas Gohr 1375bc00e11SAndreas Gohr /** 1385bc00e11SAndreas Gohr * Create or get existing Node from the tree 1395bc00e11SAndreas Gohr * 1405bc00e11SAndreas Gohr * @param Value $value 1415bc00e11SAndreas Gohr * @param int $depth 1425bc00e11SAndreas Gohr * @return NestedValue 1435bc00e11SAndreas Gohr */ 1445bc00e11SAndreas Gohr protected function getNodeForValue(Value $value, $depth) 1455bc00e11SAndreas Gohr { 1465bc00e11SAndreas Gohr $node = new NestedValue($value, $depth); 1475bc00e11SAndreas Gohr $key = (string)$node; 1485bc00e11SAndreas Gohr if (!isset($this->nodes[$key])) { 1495bc00e11SAndreas Gohr $this->nodes[$key] = $node; 1505bc00e11SAndreas Gohr } 1515bc00e11SAndreas Gohr return $this->nodes[$key]; 1525bc00e11SAndreas Gohr } 153ce44c639SAndreas Gohr 154ce44c639SAndreas Gohr /** 155ce44c639SAndreas Gohr * Create or get an existing Node for indexing 156ce44c639SAndreas Gohr * 157ce44c639SAndreas Gohr * @param Value $value 158ce44c639SAndreas Gohr * @param int $index 159ce44c639SAndreas Gohr * @return NestedValue 160ce44c639SAndreas Gohr */ 161ce44c639SAndreas Gohr protected function getIndexNode(Value $value, $index) 162ce44c639SAndreas Gohr { 163026d1098SAndreas Gohr $compare = $value->getDisplayValue(); 164ce44c639SAndreas Gohr if (is_array($compare)) $compare = $compare[0]; 165ce44c639SAndreas Gohr $key = PhpString::strtoupper(PhpString::substr($compare, 0, $index)); 166ce44c639SAndreas Gohr 167ce44c639SAndreas Gohr if (!isset($this->indexNodes[$key])) { 168ce44c639SAndreas Gohr $col = new Column( 169ce44c639SAndreas Gohr 0, 170ce44c639SAndreas Gohr new Text([], '%index%', false), 171ce44c639SAndreas Gohr -1, 172ce44c639SAndreas Gohr true, 173ce44c639SAndreas Gohr $value->getColumn()->getTable() 174ce44c639SAndreas Gohr ); 175ce44c639SAndreas Gohr $this->indexNodes[$key] = new NestedValue(new Value($col, $key), 0); 176ce44c639SAndreas Gohr } 177ce44c639SAndreas Gohr 178ce44c639SAndreas Gohr return $this->indexNodes[$key]; 179ce44c639SAndreas Gohr } 1805bc00e11SAndreas Gohr} 181