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); 112*7234bfb1Ssplitbrain if (!$valObj instanceof Value) return; // no more values to nest, usually shouldn't happen 1135bc00e11SAndreas Gohr 114ae522a2dSAndreas Gohr $parentPath = (string) $parent; 115ae522a2dSAndreas Gohr 1167b7a9290SAndreas Gohr if ($valObj->getColumn()->isMulti() && $valObj->getValue()) { 1175bc00e11SAndreas Gohr // split up multi values into separate nodes 1185bc00e11SAndreas Gohr $values = $valObj->getValue(); 1197b7a9290SAndreas Gohr if ($values) { 1205bc00e11SAndreas Gohr foreach ($values as $value) { 1215bc00e11SAndreas Gohr $newValue = new Value($valObj->getColumn(), $value); 122ae522a2dSAndreas Gohr $node = $this->getNodeForValue($newValue, $parentPath, $depth); 1235bc00e11SAndreas Gohr $parent->addChild($node); 1245bc00e11SAndreas Gohr $this->nestBranch($node, $row, $nesting, $depth + 1); 1255bc00e11SAndreas Gohr } 1265bc00e11SAndreas Gohr } else { 1277b7a9290SAndreas Gohr $newValue = new Value($valObj->getColumn(), ''); // add empty node 128ae522a2dSAndreas Gohr $node = $this->getNodeForValue($newValue, $parentPath, $depth); 1297b7a9290SAndreas Gohr $parent->addChild($node); 1307b7a9290SAndreas Gohr $this->nestBranch($node, $row, $nesting, $depth + 1); 1317b7a9290SAndreas Gohr } 1327b7a9290SAndreas Gohr } else { 133ae522a2dSAndreas Gohr $node = $this->getNodeForValue($valObj, $parentPath, $depth); 1345bc00e11SAndreas Gohr $parent->addChild($node); 1355bc00e11SAndreas Gohr $this->nestBranch($node, $row, $nesting, $depth + 1); 1365bc00e11SAndreas Gohr } 1375bc00e11SAndreas Gohr } 1385bc00e11SAndreas Gohr 1395bc00e11SAndreas Gohr /** 1405bc00e11SAndreas Gohr * Create or get existing Node from the tree 1415bc00e11SAndreas Gohr * 1425bc00e11SAndreas Gohr * @param Value $value 1435bc00e11SAndreas Gohr * @param int $depth 1445bc00e11SAndreas Gohr * @return NestedValue 1455bc00e11SAndreas Gohr */ 146ae522a2dSAndreas Gohr protected function getNodeForValue(Value $value, $parentPath, $depth) 1475bc00e11SAndreas Gohr { 148ae522a2dSAndreas Gohr $node = new NestedValue($value, $parentPath, $depth); 1495bc00e11SAndreas Gohr $key = (string)$node; 1505bc00e11SAndreas Gohr if (!isset($this->nodes[$key])) { 1515bc00e11SAndreas Gohr $this->nodes[$key] = $node; 1525bc00e11SAndreas Gohr } 1535bc00e11SAndreas Gohr return $this->nodes[$key]; 1545bc00e11SAndreas Gohr } 155ce44c639SAndreas Gohr 156ce44c639SAndreas Gohr /** 157ce44c639SAndreas Gohr * Create or get an existing Node for indexing 158ce44c639SAndreas Gohr * 159ce44c639SAndreas Gohr * @param Value $value 160ce44c639SAndreas Gohr * @param int $index 161ce44c639SAndreas Gohr * @return NestedValue 162ce44c639SAndreas Gohr */ 163ce44c639SAndreas Gohr protected function getIndexNode(Value $value, $index) 164ce44c639SAndreas Gohr { 165026d1098SAndreas Gohr $compare = $value->getDisplayValue(); 166ce44c639SAndreas Gohr if (is_array($compare)) $compare = $compare[0]; 167ce44c639SAndreas Gohr $key = PhpString::strtoupper(PhpString::substr($compare, 0, $index)); 168ce44c639SAndreas Gohr 169ce44c639SAndreas Gohr if (!isset($this->indexNodes[$key])) { 170ce44c639SAndreas Gohr $col = new Column( 171ce44c639SAndreas Gohr 0, 172ce44c639SAndreas Gohr new Text([], '%index%', false), 173ce44c639SAndreas Gohr -1, 174ce44c639SAndreas Gohr true, 175ce44c639SAndreas Gohr $value->getColumn()->getTable() 176ce44c639SAndreas Gohr ); 177ce44c639SAndreas Gohr $this->indexNodes[$key] = new NestedValue(new Value($col, $key), 0); 178ce44c639SAndreas Gohr } 179ce44c639SAndreas Gohr 180ce44c639SAndreas Gohr return $this->indexNodes[$key]; 181ce44c639SAndreas Gohr } 1825bc00e11SAndreas Gohr} 183