Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
28.89% covered (danger)
28.89%
13 / 45
25.00% covered (danger)
25.00%
2 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractBase
28.89% covered (danger)
28.89%
13 / 45
25.00% covered (danger)
25.00%
2 / 8
213.23
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setRawData
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getCount
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getData
n/a
0 / 0
n/a
0 / 0
0
 getHierarchyPositionsInParents
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
6
 getTitlesInHierarchy
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
 isCollection
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
30
 pickTitle
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 sortNodes
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3/**
4 * Hierarchy Tree Data Formatter (abstract base)
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2015.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 * @category VuFind
24 * @package  HierarchyTree_DataFormatter
25 * @author   Demian Katz <demian.katz@villanova.edu>
26 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
27 * @link     https://vufind.org/wiki/development:plugins:hierarchy_components Wiki
28 */
29
30namespace VuFind\Hierarchy\TreeDataFormatter;
31
32use function count;
33use function in_array;
34use function is_array;
35
36/**
37 * Hierarchy Tree Data Formatter (abstract base)
38 *
39 * @category VuFind
40 * @package  HierarchyTree_DataFormatter
41 * @author   Demian Katz <demian.katz@villanova.edu>
42 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
43 * @link     https://vufind.org/wiki/development:plugins:hierarchy_components Wiki
44 */
45abstract class AbstractBase implements \VuFind\I18n\HasSorterInterface
46{
47    use \VuFind\I18n\HasSorterTrait;
48
49    /**
50     * Top-level record from index
51     *
52     * @var object
53     */
54    protected $topNode;
55
56    /**
57     * Child data map from index
58     *
59     * @var array
60     */
61    protected $childMap;
62
63    /**
64     * Is sorting enabled?
65     *
66     * @var bool
67     */
68    protected $sort;
69
70    /**
71     * Collection mode
72     *
73     * @var string
74     */
75    protected $collectionType;
76
77    /**
78     * How many nodes have we formatted?
79     *
80     * @var int
81     */
82    protected $count = 0;
83
84    /**
85     * Throw an exception if hierarchy parent and sequence data is out of sync?
86     *
87     * @var bool
88     */
89    protected $validateHierarchySequences;
90
91    /**
92     * Constructor
93     *
94     * @param bool $validateHierarchySequences Throw an exception if hierarchy parent and sequence data is out of sync?
95     */
96    public function __construct($validateHierarchySequences = true)
97    {
98        $this->validateHierarchySequences = $validateHierarchySequences;
99    }
100
101    /**
102     * Set raw data.
103     *
104     * @param object $topNode  Full record for top node
105     * @param array  $childMap Data map from index
106     * @param bool   $sort     Is sorting enabled?
107     * @param string $cType    Collection type
108     *
109     * @return void
110     */
111    public function setRawData($topNode, $childMap, $sort = false, $cType = 'All')
112    {
113        $this->topNode = $topNode;
114        $this->childMap = $childMap;
115        $this->sort = $sort;
116        $this->collectionType = $cType;
117    }
118
119    /**
120     * Get number of nodes formatted.
121     *
122     * @return int
123     */
124    public function getCount()
125    {
126        return $this->count;
127    }
128
129    /**
130     * Get the formatted metadata.
131     *
132     * @return string
133     */
134    abstract public function getData();
135
136    /**
137     * Get the positions of this item within parent collections. Returns an array
138     * of parent ID => sequence number.
139     *
140     * @param object $fields Solr fields
141     *
142     * @return array
143     */
144    protected function getHierarchyPositionsInParents($fields)
145    {
146        $retVal = [];
147        if (
148            isset($fields->hierarchy_parent_id)
149            && isset($fields->hierarchy_sequence)
150        ) {
151            $parentIDs = $fields->hierarchy_parent_id;
152            $sequences = $fields->hierarchy_sequence;
153
154            if (count($parentIDs) > count($sequences)) {
155                if ($this->validateHierarchySequences) {
156                    throw new \Exception('Fields hierarchy_parent_id and hierarchy_sequence have different lengths.');
157                } else {
158                    return [];
159                }
160            }
161
162            foreach ($parentIDs as $key => $val) {
163                $retVal[$val] = $sequences[$key];
164            }
165        }
166        return $retVal;
167    }
168
169    /**
170     * Get the titles of this item within parent collections. Returns an array
171     * of parent ID => sequence number.
172     *
173     * @param object $fields Solr fields
174     *
175     * @return Array
176     */
177    protected function getTitlesInHierarchy($fields)
178    {
179        $retVal = [];
180        if (
181            isset($fields->title_in_hierarchy)
182            && is_array($fields->title_in_hierarchy)
183        ) {
184            $titles = $fields->title_in_hierarchy;
185            $parentIDs = (array)($fields->hierarchy_parent_id ?? []);
186            if (count($titles) === count($parentIDs)) {
187                foreach ($parentIDs as $key => $val) {
188                    $retVal[$val] = $titles[$key];
189                }
190            }
191        }
192        return $retVal;
193    }
194
195    /**
196     * Identify whether the provided record is a collection.
197     *
198     * NOTE: \VuFind\RecordDriver\SolrDefault::isCollection() duplicates some of\
199     * this logic.
200     *
201     * @param object $fields Solr fields
202     *
203     * @return bool
204     */
205    protected function isCollection($fields)
206    {
207        // Check config setting for what constitutes a collection
208        switch ($this->collectionType) {
209            case 'All':
210                return isset($fields->is_hierarchy_id);
211            case 'Top':
212                return isset($fields->is_hierarchy_id)
213                    && in_array($fields->is_hierarchy_id, $fields->hierarchy_top_id);
214            default:
215                // Default to not be a collection level record
216                return false;
217        }
218    }
219
220    /**
221     * Choose a title for the record.
222     *
223     * @param object $record   Solr record to format
224     * @param string $parentID The starting point for the current recursion
225     * (equivalent to Solr field hierarchy_parent_id)
226     *
227     * @return string
228     */
229    protected function pickTitle($record, $parentID)
230    {
231        if (null !== $parentID) {
232            $titles = $this->getTitlesInHierarchy($record);
233            if (isset($titles[$parentID])) {
234                return $titles[$parentID];
235            }
236        }
237        // TODO: handle missing titles more gracefully (title not available?)
238        return $record->title ?? $record->id;
239    }
240
241    /**
242     * Sort Nodes
243     * Convert an unsorted array of [ key, value ] pairs into a sorted array
244     * of values.
245     *
246     * @param array $array The array of arrays to sort
247     *
248     * @return array
249     */
250    protected function sortNodes($array)
251    {
252        // Sort arrays based on first element
253        $sorter = function ($a, $b) {
254            return $this->getSorter()->compare($a[0], $b[0]);
255        };
256        usort($array, $sorter);
257
258        // Collapse array to remove sort values
259        $mapper = function ($i) {
260            return $i[1];
261        };
262        return array_map($mapper, $array);
263    }
264}