xref: /plugin/struct/meta/NestedValue.php (revision 1ee0a2dc290e607b00cb9d6d919f4f60a00fcd2d)
15bc00e11SAndreas Gohr<?php
25bc00e11SAndreas Gohr
35bc00e11SAndreas Gohrnamespace dokuwiki\plugin\struct\meta;
45bc00e11SAndreas Gohr
55bc00e11SAndreas Gohruse dokuwiki\Utf8\Sort;
65bc00e11SAndreas Gohr
75bc00e11SAndreas Gohr/**
85bc00e11SAndreas Gohr * Object to create a tree of values
95bc00e11SAndreas Gohr *
105bc00e11SAndreas Gohr * You should not create these yourself, but use the NestedResult class instead
115bc00e11SAndreas Gohr */
125bc00e11SAndreas Gohrclass NestedValue
135bc00e11SAndreas Gohr{
145bc00e11SAndreas Gohr
155bc00e11SAndreas Gohr    /** @var Value */
165bc00e11SAndreas Gohr    protected $value;
175bc00e11SAndreas Gohr
185bc00e11SAndreas Gohr    /** @var NestedValue[] */
195bc00e11SAndreas Gohr    protected $children = [];
205bc00e11SAndreas Gohr
215bc00e11SAndreas Gohr    /** @var Value[][] */
225bc00e11SAndreas Gohr    protected $resultRows = [];
235bc00e11SAndreas Gohr
245bc00e11SAndreas Gohr    /** @var int the nesting depth */
255bc00e11SAndreas Gohr    protected $depth;
265bc00e11SAndreas Gohr
275bc00e11SAndreas Gohr    /**
285bc00e11SAndreas Gohr     * Create a nested version of the given value
295bc00e11SAndreas Gohr     *
305bc00e11SAndreas Gohr     * @param Value|null $value The value to store, null for root node
315bc00e11SAndreas Gohr     * @param int $depth The depth of this node (avoids collision where the same values are selected on multiple levels)
325bc00e11SAndreas Gohr     */
335bc00e11SAndreas Gohr    public function __construct(?Value $value, $depth = 0)
345bc00e11SAndreas Gohr    {
355bc00e11SAndreas Gohr        $this->value = $value;
365bc00e11SAndreas Gohr        $this->depth = $depth;
375bc00e11SAndreas Gohr    }
385bc00e11SAndreas Gohr
395bc00e11SAndreas Gohr    /**
405bc00e11SAndreas Gohr     * @return int
415bc00e11SAndreas Gohr     */
425bc00e11SAndreas Gohr    public function getDepth()
435bc00e11SAndreas Gohr    {
445bc00e11SAndreas Gohr        return $this->depth;
455bc00e11SAndreas Gohr    }
465bc00e11SAndreas Gohr
475bc00e11SAndreas Gohr    /**
485bc00e11SAndreas Gohr     * Access the stored value
495bc00e11SAndreas Gohr     *
505bc00e11SAndreas Gohr     * @return Value|null the value stored in this node, null for root node
515bc00e11SAndreas Gohr     */
525bc00e11SAndreas Gohr    public function getValueObject()
535bc00e11SAndreas Gohr    {
545bc00e11SAndreas Gohr        return $this->value;
555bc00e11SAndreas Gohr    }
565bc00e11SAndreas Gohr
575bc00e11SAndreas Gohr    /**
585bc00e11SAndreas Gohr     * Add a child node
595bc00e11SAndreas Gohr     *
605bc00e11SAndreas Gohr     * Nodes with the same key (__toString()) will be overwritten
615bc00e11SAndreas Gohr     *
625bc00e11SAndreas Gohr     * @param NestedValue $child
635bc00e11SAndreas Gohr     * @return void
645bc00e11SAndreas Gohr     */
655bc00e11SAndreas Gohr    public function addChild(NestedValue $child)
665bc00e11SAndreas Gohr    {
675bc00e11SAndreas Gohr        $this->children[(string)$child] = $child; // ensures uniqueness
685bc00e11SAndreas Gohr    }
695bc00e11SAndreas Gohr
705bc00e11SAndreas Gohr    /**
715bc00e11SAndreas Gohr     * Get all child nodes
725bc00e11SAndreas Gohr     *
735bc00e11SAndreas Gohr     * @return NestedValue[]
745bc00e11SAndreas Gohr     */
755bc00e11SAndreas Gohr    public function getChildren()
765bc00e11SAndreas Gohr    {
775bc00e11SAndreas Gohr        $children = $this->children;
785bc00e11SAndreas Gohr        usort($children, [$this, 'sortChildren']);
795bc00e11SAndreas Gohr        return $children;
805bc00e11SAndreas Gohr    }
815bc00e11SAndreas Gohr
825bc00e11SAndreas Gohr    /**
835bc00e11SAndreas Gohr     * Add a result row to this node
845bc00e11SAndreas Gohr     *
85*1ee0a2dcSAndreas Gohr     * Only unique rows will be stored, duplicates are detected by hashing the row values' toString result
86*1ee0a2dcSAndreas Gohr     *
875bc00e11SAndreas Gohr     * @param Value[] $row
885bc00e11SAndreas Gohr     * @return void
895bc00e11SAndreas Gohr     */
905bc00e11SAndreas Gohr    public function addResultRow($row)
915bc00e11SAndreas Gohr    {
92*1ee0a2dcSAndreas Gohr        // only add unique rows
93*1ee0a2dcSAndreas Gohr        $ident = md5(array_reduce($row, function ($carry, $value) {
94*1ee0a2dcSAndreas Gohr            return $carry . $value;
95*1ee0a2dcSAndreas Gohr        }, ''));
96*1ee0a2dcSAndreas Gohr
97*1ee0a2dcSAndreas Gohr        $this->resultRows[$ident] = $row;
985bc00e11SAndreas Gohr    }
995bc00e11SAndreas Gohr
1005bc00e11SAndreas Gohr    /**
1015bc00e11SAndreas Gohr     * Get all result rows stored in this node
1025bc00e11SAndreas Gohr     *
1035bc00e11SAndreas Gohr     * @return Value[][]
1045bc00e11SAndreas Gohr     */
1055bc00e11SAndreas Gohr    public function getResultRows()
1065bc00e11SAndreas Gohr    {
107*1ee0a2dcSAndreas Gohr        return array_values($this->resultRows);
1085bc00e11SAndreas Gohr    }
1095bc00e11SAndreas Gohr
1105bc00e11SAndreas Gohr    /**
1115bc00e11SAndreas Gohr     * Get a unique key for this node
1125bc00e11SAndreas Gohr     *
1135bc00e11SAndreas Gohr     * @return string
1145bc00e11SAndreas Gohr     */
1155bc00e11SAndreas Gohr    public function __toString()
1165bc00e11SAndreas Gohr    {
1175bc00e11SAndreas Gohr        if ($this->value === null) return ''; // root node
1185bc00e11SAndreas Gohr        return $this->value->__toString() . '-' . $this->depth;
1195bc00e11SAndreas Gohr    }
1205bc00e11SAndreas Gohr
1215bc00e11SAndreas Gohr    /**
1225bc00e11SAndreas Gohr     * Custom comparator to sort the children of this node
1235bc00e11SAndreas Gohr     *
1245bc00e11SAndreas Gohr     * @param NestedValue $a
1255bc00e11SAndreas Gohr     * @param NestedValue $b
1265bc00e11SAndreas Gohr     * @return int
1275bc00e11SAndreas Gohr     */
1285bc00e11SAndreas Gohr    public function sortChildren(NestedValue $a, NestedValue $b)
1295bc00e11SAndreas Gohr    {
1305bc00e11SAndreas Gohr        // note: the way NestedResults build the NestedValues, the value object should
1315bc00e11SAndreas Gohr        // always contain a single value only. But since the associated column is still
1325bc00e11SAndreas Gohr        // a multi-value column, getCompareValue() will still return an array.
1335bc00e11SAndreas Gohr        // So here we treat all returns as array and join them with a dash (even though
1345bc00e11SAndreas Gohr        // there should never be more than one value in there)
1355bc00e11SAndreas Gohr        return Sort::strcmp(
1365bc00e11SAndreas Gohr            join('-', (array)$a->getValueObject()->getCompareValue()),
1375bc00e11SAndreas Gohr            join('-', (array)$b->getValueObject()->getCompareValue())
1385bc00e11SAndreas Gohr        );
1395bc00e11SAndreas Gohr    }
1405bc00e11SAndreas Gohr
141*1ee0a2dcSAndreas Gohr    /**
142*1ee0a2dcSAndreas Gohr     * print the tree for debugging
143*1ee0a2dcSAndreas Gohr     *
144*1ee0a2dcSAndreas Gohr     * @return string
145*1ee0a2dcSAndreas Gohr     */
146*1ee0a2dcSAndreas Gohr    public function dump()
147*1ee0a2dcSAndreas Gohr    {
148*1ee0a2dcSAndreas Gohr        $return = '';
149*1ee0a2dcSAndreas Gohr
150*1ee0a2dcSAndreas Gohr        if ($this->value) {
151*1ee0a2dcSAndreas Gohr            $return .= str_pad('', $this->getDepth() * 4, ' ');
152*1ee0a2dcSAndreas Gohr            $return .= join(', ', (array)$this->value->getDisplayValue());
153*1ee0a2dcSAndreas Gohr            $return .= "\n";
154*1ee0a2dcSAndreas Gohr        } else {
155*1ee0a2dcSAndreas Gohr            $return .= "*\n";
156*1ee0a2dcSAndreas Gohr        }
157*1ee0a2dcSAndreas Gohr
158*1ee0a2dcSAndreas Gohr        foreach ($this->getResultRows() as $row) {
159*1ee0a2dcSAndreas Gohr            $return .= str_pad('', $this->getDepth() * 4, ' ');
160*1ee0a2dcSAndreas Gohr            foreach ($row as $value) {
161*1ee0a2dcSAndreas Gohr                $return .= ' ' . join(', ', (array)$value->getDisplayValue());
162*1ee0a2dcSAndreas Gohr            }
163*1ee0a2dcSAndreas Gohr            $return .= "\n";
164*1ee0a2dcSAndreas Gohr        }
165*1ee0a2dcSAndreas Gohr
166*1ee0a2dcSAndreas Gohr        foreach ($this->getChildren() as $child) {
167*1ee0a2dcSAndreas Gohr            $return .= $child->dump();
168*1ee0a2dcSAndreas Gohr        }
169*1ee0a2dcSAndreas Gohr
170*1ee0a2dcSAndreas Gohr        return $return;
171*1ee0a2dcSAndreas Gohr    }
1725bc00e11SAndreas Gohr}
173