-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathCollectionTree.php
More file actions
405 lines (352 loc) · 13 KB
/
CollectionTree.php
File metadata and controls
405 lines (352 loc) · 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
<?php
/**
* Collection Tree
*
* @copyright Copyright 2007-2012 Roy Rosenzweig Center for History and New Media
* @license http://www.gnu.org/licenses/gpl-3.0.txt GNU GPLv3
*/
/**
* The collection_trees table.
*
* @package Omeka\Plugins\CollectionTree
*/
class Table_CollectionTree extends Omeka_Db_Table
{
/**
* Cache of all collections, including table and hierarchy data.
*
* Load this cache only during actions that must process hierarchical
* collection data.
*/
protected $_collections;
/**
* Cache of children of a collection.
*
* It's an associative array where keys are collection ids and values are
* arrays of children ids.
*/
protected $_collectionsChildren;
/**
* Cache of variables needed for some use.
*
* Caching is often needed to extract variables from recursive methods. Be
* sure to reset the cache when it's no longer needed using
* self::_resetCache().
*
* @see self::getDescendantTree()
*/
protected $_cache = array();
/**
* Fetch all collections that can be assigned as a parent collection to the
* specified collection.
*
* All collections that are not the specified collection and not children of
* the specified collection can be parent collections.
*
* @param null|int $collectionId
* @return array An array of collection rows.
*/
public function fetchAssignableParentCollections($collectionId)
{
// Must cast null collection ID to 0 to properly bind.
$collectionId = (int) $collectionId;
$table = $this->_db->getTable('Collection');
$alias = $this->getTableAlias();
$aliasCollection = $table->getTableAlias();
// Access rights to collections are automatically managed.
$select = $table->getSelect();
$select->joinLeft(
array($alias => $this->getTableName()),
"$aliasCollection.id = $alias.collection_id",
array('name'));
$select->where("$aliasCollection.id != ?", $collectionId);
// If not a new collection, cache descendant collection IDs and exclude
// those collections from the result.
if ($collectionId) {
$unassignableCollectionIds = $this->getUnassignableCollectionIds();
if ($unassignableCollectionIds) {
$select->where("$aliasCollection.id NOT IN (?)", $unassignableCollectionIds);
}
}
// Order alphabetically if configured to do so.
if (get_option('collection_tree_alpha_order')) {
$select->order("$alias.name ASC");
}
return $this->fetchAssoc($select);
}
/**
* Find parent/child relationship by collection ID.
*
* @param int $childCollectionId
* @return Omeka_Record
*/
public function findByCollectionId($collectionId)
{
// Cast to integer to prevent SQL injection.
$collectionId = (int) $collectionId;
$alias = $this->getTableAlias();
$select = $this->getSelect();
$select->where("$alias.collection_id = ?", $collectionId);
$select->limit(1);
$select->reset(Zend_Db_Select::ORDER);
// Child collection IDs are unique, so only fetch one row.
return $this->fetchObject($select);
}
/**
* Find parent/child relationships by parent collection ID.
*
* @param int $parentCollectionId
* @return array
*/
public function findByParentCollectionId($parentCollectionId)
{
// Cast to integer to prevent SQL injection.
$parentCollectionId = (int) $parentCollectionId;
$alias = $this->getTableAlias();
$select = $this->getSelect();
$select->where("$alias.parent_collection_id = ?", $parentCollectionId);
$select->reset(Zend_Db_Select::ORDER);
return $this->fetchObjects($select);
}
/**
* Return the collection tree hierarchy as a one-dimensional array.
*
* @param array $options (optional) Set of parameters for searching/
* filtering results.
* @param string $padding The string representation of the collection depth.
* @return array
*/
public function findPairsForSelectForm(array $options = array(), $padding = '-')
{
if (isset($params['padding'])) {
$padding = $params['padding'];
} else {
$padding = '-';
}
$options = array();
foreach ($this->getRootCollections() as $rootCollectionId => $rootCollection) {
$options[$rootCollectionId] = $rootCollection['name'] ? $rootCollection['name'] : __('[Untitled]');
$this->_resetCache();
$this->getDescendantTree($rootCollectionId, true);
foreach ($this->_cache as $collectionId => $collectionDepth) {
$collection = $this->getCollection($collectionId);
$options[$collectionId] = str_repeat($padding, $collectionDepth) . ' ';
$options[$collectionId] .= $collection['name'] ? $collection['name'] : __('[Untitled]');
}
}
$this->_resetCache();
return $options;
}
/**
* Get the entire collection tree of the specified collection.
*
* @param int $collectionId
* @return array
*/
public function getCollectionTree($collectionId)
{
return $this->getAncestorTree($collectionId, true);
}
/**
* Get the ancestor tree or the entire collection tree of the specified
* collection.
*
* @param int $collectionId
* @param bool $getCollectionTree Include the passed collection, its
* ancestor tree, and its descendant tree.
* @return array
*/
public function getAncestorTree($collectionId, $getCollectionTree = false)
{
$tree = array();
// Distinguish between the passed collection and its descendants.
$parentCollectionId = $collectionId;
// Iterate the parent collections, starting with the passed collection
// and stopping at the root collection.
do {
$collection = $this->getCollection($parentCollectionId);
$parentCollectionId = $collection['parent_collection_id'];
// Don't include the passed collection when not building the entire
// collection tree.
if (!$getCollectionTree && $collectionId == $collection['id']) {
continue;
}
// If set to return the entire collection tree, add the descendant
// tree to the passed collection and flag it as current.
if ($getCollectionTree && $collectionId == $collection['id']) {
$collection['children'] = $this->getDescendantTree($collection['id']);
$collection['current'] = true;
}
// Prepend the parent collection to the collection tree, pushing the
// descendant tree to the second element.
array_unshift($tree, $collection);
// Save the descendant tree as children of the parent collection and
// remove the extraneous descendant tree.
if (isset($tree[1])) {
$tree[0]['children'] = array($tree[1]);
unset($tree[1]);
}
} while ($collection['parent_collection_id']);
return $tree;
}
/**
* Recursively get the descendant tree of the specified collection.
*
* @param int $collectionId
* @param bool $cacheDescendantInfo Cache IDs and depth of all descendant
* collections?
* @param int $collectionDepth The initial depth of the collection.
* @return array
*/
public function getDescendantTree($collectionId, $cacheDescendantInfo = false, $collectionDepth = 0)
{
// Increment the collection depth.
$collectionDepth++;
// Iterate the child collections.
$descendantTree = array_values($this->getChildCollections($collectionId));
for ($i = 0; $i < count($descendantTree); $i++) {
if ($cacheDescendantInfo) {
$this->_cache[$descendantTree[$i]['id']] = $collectionDepth;
}
// Recurse the child collections, getting their children.
$children = $this->getDescendantTree(
$descendantTree[$i]['id'],
$cacheDescendantInfo,
$collectionDepth);
// Assign the child collections to the descendant tree.
if ($children) {
$descendantTree[$i]['children'] = $children;
} else {
$descendantTree[$i]['children'] = array();
}
}
return $descendantTree;
}
/**
* Get the specified collection.
*
* @param int $collectionId
* @return array|bool
*/
public function getCollection($collectionId)
{
$collections = $this->_getCollections();
return isset($collections[$collectionId]) ? $collections[$collectionId] : false;
}
/**
* Get the child collections of the specified collection.
*
* @param int $collectionId
* @return array Associative array of collections, by id.
*/
public function getChildCollections($collectionId)
{
$childCollections = array();
$collectionsChildren = $this->_getCollectionsChildren();
if (isset($collectionsChildren[$collectionId])) {
foreach ($collectionsChildren[$collectionId] as $childId) {
$childCollections[$childId] = $this->getCollection($childId);
}
}
return $childCollections;
}
/**
* Get the list of descendant collections and the selected one.
*
* @param int $collectionId
* @return array Associative array of collections.
*/
public function getDescendantOrSelfCollections($collectionId)
{
$collections = array();
$rootCollection = $this->getCollection($collectionId);
if ($rootCollection) {
$this->_resetCache();
$this->getDescendantTree($collectionId, true);
$collections[$collectionId] = $rootCollection;
$collections += array_intersect_key($this->_getCollections(), $this->_cache);
$this->_resetCache();
}
return $collections;
}
/**
* Get all root collections, i.e. those without parent collections.
*
* @return array Associative array of root collections, by id.
*/
public function getRootCollections()
{
$rootCollections = array();
$collections = $this->_getCollections();
foreach ($collections as $collection) {
if (!$collection['parent_collection_id']) {
$rootCollections[$collection['id']] = $collection;
}
}
return $rootCollections;
}
/**
* Get all collection IDs to which the passed collection cannot be assigned.
*
* A collection cannot be assigned to a collection in its descendant tree,
* including itself.
*
* @param int $collectionId
* @return array
*/
public function getUnassignableCollectionIds($collectionId)
{
$this->_resetCache();
$this->getDescendantTree($collectionId, true);
$unassignableCollections = array_keys($this->_cache);
$this->_resetCache();
return $unassignableCollections;
}
/**
* Cache collection data with name and parent id in an associative array.
*/
protected function _getCollections()
{
if (is_null($this->_collections)) {
$table = $this->_db->getTable('Collection');
$alias = $this->getTableAlias();
$aliasCollection = $table->getTableAlias();
// Access rights to collections are automatically managed.
$select = $table->getSelect();
$select->joinLeft(
array($alias => $this->getTableName()),
"$aliasCollection.id = $alias.collection_id",
array('parent_collection_id', 'name'));
// Order alphabetically if configured to do so.
if (get_option('collection_tree_alpha_order')) {
$select->order("$alias.name ASC");
}
$this->_collections = $this->fetchAssoc($select);
}
return $this->_collections;
}
/**
* Cache collections children data in an associative array.
*/
protected function _getCollectionsChildren()
{
if (is_null($this->_collectionsChildren)) {
$collections = $this->_getCollections();
$this->_collectionsChildren = array();
foreach ($collections as $id => $collection) {
if ($collection['parent_collection_id']) {
$parent_collection_id = $collection['parent_collection_id'];
$this->_collectionsChildren[$parent_collection_id][] = $id;
}
}
}
return $this->_collectionsChildren;
}
/**
* Reset the cache property.
*/
protected function _resetCache()
{
$this->_cache = array();
}
}